I am working on my first Meteor app that uses OAuth for login. Prior to this, all of my projects have used only the accounts-password portions.
I have a simple login for with a button to login via Google:
<template name="login">
<form id="login" role="form">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" />
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" />
</div>
<button id="signin" class="btn btn-primary">Sign in</button>
<hr />
Or:<br />
<button id="loginWithFacebook">Login with Facebook</button>
<button id="loginWithGoogle">Login with Google</button>
<button id="loginWithTwitter">Login with Twitter</button>
</form>
</template>
I then have a event handler to capture the button click and call the loginWithGoogle:
...
"click #loginWithGoogle": function(e, t){
Meteor.loginWithGoogle({
requestPermissions: [],
loginStyle: "popup"
}, function(err) {
if (err) {
// TODO Need to do something here with the error...
console.log('Error: ', err);
} else {
Router.go('home');
}
});
}
...
On the server I setup OAuth for Google like thus:
ServiceConfiguration.configurations.remove({
service: "google"
});
ServiceConfiguration.configurations.insert({
service: "google",
clientId: "000000000000000",
loginStyle: "popup",
secret: "000000000"
});
Accounts.onCreateUser(function (options, user) {
console.log('Creating user: ' + user.username);
return user;
});
And in my route I have this:
if (Meteor.isClient) {
// Initialize the loading template before hand
Router.onBeforeAction('loading');
// Map the routes
Router.map(function() {
// Homepage
this.route('home', {
path: '/',
onBeforeAction: function() {
if (!Meteor.user()) {
console.log('User is not logged in. Displaying login form.');
Router.go('login');
} else {
console.log('User is already logged in:', Meteor.user());
}
}
});
// Login page
this.route('login', {
path: '/login'
});
});
}
So, I get the console log in the browser saying User is not logged in. Displaying login form., and when I click the Login with Google button I get the popup asking which Google account I want to use, then the confirmation page. But when I click the Accept button I get nothing. The Accounts.onCreateUser() doesn't seem to run, nor does the callback code on the loginWithGoogle(). How do I get this configured correctly so that the callback runs, and ultimately I am re-directed back to my homepage?
Your onCreateUser doesn't work because you don't have username specified.
Try using
if(user.services.google)
user.username = user.services.google.name
And by the way, why won't you use {{>loginButtons}}?
Related
//Client side code in client\main.js
Tasks=new Mongo.Collection('tasks');
Template.tasks.helpers({
tasks:function () {
return Tasks.find({},{sort:{createdAt:-1}});
}
});
Template.tasks.events({
"submit .add-task":function (event) {
var name = event.target.name.value;
Meteor.call('addTask',name);
event.target.name.value='';
return false;
},
"click .delete-task":function (event) {
if(confirm('Delete Task?')){
Meteor.call('deleteTask',this._id);
}
return false;
}
});
Meteor.methods({
addTask: function (name) {
if (!Meteor.userId()) {
throw new Meteor.Error('No Access!!');
}
Tasks.insert({
name: name,
createdAt: new Date(),
userId: Meteor.userId()
});
},
deleteTask: function(taskId) {
Tasks.remove(taskId);
}
});
//Server side code in server\main.js
Tasks=new Mongo.Collection('tasks');
Meteor.methods({
addTask: function (name) {
if (!Meteor.userId()) {
throw new Meteor.Error('No Access!!');
}
Tasks.insert({
name: name,
createdAt: new Date(),
userId: Meteor.userId()
});
},
deleteTask: function taskId() {
Tasks.remove(taskId);
}
});
//Html page
<head>
<title>tasklist</title>
</head>
<body>
{{> tasks}}
</body>
<template name="tasks">
{{> loginButtons}}
<h1>Add Task</h1>
{{#if currentUser}}
<form class="add-task">
<label>Task Name</label>
<input type="text" name="name" placeholder="Add Task" />
<input type="submit" value="Submit" />
</form>
{{else}}
<p>Please log in to add tasks</p>
{{/if}}
<hr />
<h3>Tasks</h3>
<ul>
{{#each tasks}}
<li>{{name}}{{#if currentUser}} X{{/if}} </li>
{{/each}}
</ul>
</template>
Please help, in this when i reload page it first add and and shows in the web-page and if i delete the try todelete the added then screen doesnot show even added tasks which are in MongoDB.
And when i do console, there is an empty array of added tasks
If it "blinks" this often means that your Method worked client side but not server side. The insert worked on your Minimongo (client) but not on the real MongoDB (server). So Meteor decide to rollback your insert on Minimongo.
You must have a problem inside your Method server side:
You can't use Meteor.userId() server side in your Methods, you have to use this.userId.
To avoid mistakes, only use this.userId inside the Meteor Methods client or server side.
// Server side
Meteor.methods({
addTask: function (name) {
if (!this.userId) {
throw new Meteor.Error('No Access!!');
}
Tasks.insert({
name: name,
createdAt: new Date(),
userId: this.userId
});
},
deleteTask: function taskId() {
Tasks.remove(taskId);
}
});
In Meteor, you don't have to duplicate your Methods client AND server side.
Methods should always be defined in common code loaded on the client and the server to enable Optimistic UI.
You just have to define your Methods and your collection one time inside a folder loaded on client and server. For exemple you can put your code inside a folder named both/.
I am new to meteor and got stucked and can't understand what am I doing wrong. Enlighten me please.
Here is the HTML file:
<body>
<h1>Do</h1>
{{#if activeTask}}
{{> currentTask}}
{{else}}
{{> newTask }}
{{/if}}
<div>
</div>
</body>
<template name="newTask">
<form>
<label>What<input type="text" name="what" placeholder="gimme an action"/></label>
<input type="submit" value="Go"/>
</form>
<!--
{{> inputAutocomplete settings=settings id="msg" class="input-xlarge" placeholder="action"}}
-->
</template>
<template name="currentTask">
<form>
<label>What<input type="text" name="what" placeholder="gimme an action"/>{{activeTask.what}}</label>
<div>4h 15m</div>
<input type="submit" value="Stop"/>
</form>
</template>
And here is the JavaScript file:
tasks = new Mongo.Collection('tasks');
if (Meteor.isClient) {
Template.body.helpers({
activeTask: function() {
var task = tasks.findOne(
{
endAt: null
},
{
sort: {
startAt: -1
}
}
);
console.log(task);
return task;
}
});
Template.newTask.events({
'submit' : function(event) {
event.preventDefault();
var now = Date.now();
var what = event.target.what.value;
tasks.insert({ what: what, startAt: now, endAt: null });
}
});
}
It successfully adds a new document into the database and logs this in the helper activeTask. One step later it logs no task at all. It has gone. But why?
If you don't have the package autopublish (https://atmospherejs.com/meteor/autopublish) installed, you need to create a publication (client-side) and subscription (server-side):
if (Meteor.isServer) {
Meteor.publish('tasks', function () {
return tasks.find();
});
}
if (Meteor.isClient) {
Meteor.subscribe('tasks');
}
I've also explained working with collections in a recent blog article.
I am having a little bit of trouble outputting a image after a filepicker selection with the file picker plus package from meteor. How to I grab the uploaded image url or file path, so I can pass it into a form input and put it in a collection. Putting in into the collection isnt the part I am worried about its getting the path that I am having trouble with cheers.
All contained in postSubmit template.
I have a form with
<input type="filepicker" name="myName" />
and a img output in the same template
<img src="{{filepickerIdToUrl myName}}">
and a router file containg
Router.onBeforeAction(function(){
loadFilePicker('magickey');
//can leave out key if its in settings
this.next();
},{only:['postSubmit']});
Here is the full postSubmit template
<template name="postSubmit">
<form>
<label for="title">Title</label>
<input name="title" id="title" type="text" value="" placeholder="Name your post"/>
<button id="uploadImage" class="btn btn-info btn-sm"><i class="fa fa-upload"></i> Upload</button>
<input type="submit" value="Submit"/>
</form>
<img id="imagePreview" class="img-responsive" src="{{filepickerIdToImageUrl imageId placehold_it='500x350' h=200 w=300}}"/>
<button id="removeImage" class="btn btn-warning btn-sm"><i class="fa fa-trash-o"></i> Remove</button>
This is also my postSubmit events
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
title: $(e.target).find('[name=title]').val(),
image: $(e.target).find('[name=image]').val()
};
Meteor.call('postInsert', post, function(error, result) {
// display the error to the user and abort
if (error)
return alert(error.reason);
Router.go('postPage', {_id: result._id});
});
}
});
Thanks to Nate and the google groups link above, I got it working.
Here is my solved code, Right now it only shows the preview on the form and you can remove it by clearing the session value, but it will be easy enough to grab that session value and put it into the form on submit.
Thanks again Nate.
Template.postSubmit.created = function(){
Session.setDefault("imageId", null);
Session.setDefault("imageKey", null);
};
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
title: $(e.target).find('[name=title]').val(),
image: $(e.target).find('[name=image]').val()
};
Meteor.call('postInsert', post, function(error, result) {
// display the error to the user and abort
if (error)
return alert(error.reason);
Router.go('postPage', {_id: result._id});
});
},
'click #uploadImage':function(event, template){
event.preventDefault();
filepicker.pickAndStore(
{
mimetypes: ['image/gif','image/jpeg','image/png'],
multiple: false
},{
access:"public"
},
function(InkBlobs){
// the upload is now complete to filepicker - but the form hasnt persisted the values to our collection yet
Session.set("imageId", _.last(_.first(InkBlobs).url.split("/")));
Session.set("imageKey", _.first(InkBlobs).key);
// once the session changes are made, the form will now have the new values, including a preview of the image uploaded
},
function(FPError){
log.error(FPError.toString());
}
);
},
'click #removeImage':function(event, template){
event.preventDefault();
Session.set("imageId", "remove");
Session.set("imageKey", "remove");
}
});
Template.postSubmit.helpers({
'hideRemove':function(){
return Session.equals("imageId", null) || Session.equals("imageId", "remove");
},
'imageId':function(){
if(Session.equals("imageId", "remove"))
return "";
else
return Session.get("imageId") || "";
},
'imageKey':function(){
if(Session.equals("imageKey", "remove"))
return "";
else
return Session.get("imageKey") || "";
}
});
I'm building an application that doesn't support public registration. New users will be created by an existing user and an enrollment email will be sent to the new user's email address. I've got this all working, but my implementation feels hackish.
Here's the code, so far.
Template:
<template name="addUser">
<form role="form">
<div class="form-group">
<label for="firstName">First Name</label>
<input name="firstName" class="form-control" placeholder="First Name" />
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input name="lastName" class="form-control" placeholder="Last Name" />
</div>
<div class="form-group">
<label for="email">Email</label>
<input name="email" class="form-control" placeholder="Email" />
</div>
<button type="submit" class="btn btn-primary">{{faIcon 'plus'}} Add User</button>
</form>
</template>
Template's Javascript:
Template.addUser.events({
'submit form': function (e, t) {
e.preventDefault();
var attrs = {
email: t.find('[name="email"]').value,
profile: {
firstName: t.find('[name="firstName"]').value,
lastName: t.find('[name="lastName"]').value,
}
};
Meteor.call('addUser', attrs, function (err) {
if (err) {
Errors.throw(err.reason);
} else {
Router.go('home');
}
});
}
});
My addUser method
Meteor.methods({
addUser: function (attrs) {
var user = Meteor.user();
if (!user) throw new Meteor.Error(401, 'Please login.');
if (!attrs.profile.firstName) throw new Meteor.Error(422, 'Please include a first name.');
if (!attrs.profile.lastName) throw new Meteor.Error(422, 'Please include a first name.');
if (!attrs.email) throw new Meteor.Error(422, 'Please include an email.');
var user = _.pick(attrs, ['firstName', 'lastName', 'email']);
if (Meteor.isServer) {
var newUserId = Accounts.createUser(attrs);
Accounts.sendEnrollmentEmail(newUserId);
}
}
});
Since Accounts.createUser requires a password on the client side, I can't figure out how to notify the client of success or failure short of doing something hacky with Session. What's a good way to go about doing such a thing?
First, put addUser method on server only (i.e. in server dir):
Meteor.methods({
addUser: function (attrs) {
var user = Meteor.user();
if (!user) // you can also check this.userId here
throw new Meteor.Error(401, 'Please login.');
if (!attrs.profile.firstName)
throw new Meteor.Error(422, 'Please include a first name.');
if (!attrs.profile.lastName)
throw new Meteor.Error(422, 'Please include a first name.');
if (!attrs.email)
throw new Meteor.Error(422, 'Please include an email.');
var user = _.pick(attrs, ['firstName', 'lastName', 'email']);
var newUserId = Accounts.createUser(attrs);
Accounts.sendEnrollmentEmail(newUserId);
}
});
Then, on the client, you can do something like:
Meteor.call('addUser', attrs, function (err) {
if (err) {
console.log('something went wrong :(');
}
});
Give the user a default password.
(I recommend using the check() function inside your addUser method – replacing some of your if statements. Also, you don't need Meteor.isServer as long as you put the js-file inside the /server folder.)
How to pass form parameters into spring controller.
I need to submit the below form into this url "submit.htm".
<form data-bind="submit: save" action="forms/submit.htm" method="post">
<fieldset>
<legend>User: <span data-bind='text: errors().length'></span> errors</legend>
<label>First name: <input data-bind='value: firstName'/></label>
<label>Last name: <input data-bind='value: lastName'/></label>
</fieldset>
<button type="submit">Go</button>
</form>
JS:
var viewModel = {
firstName: ko.observable().extend({
required: true,
minLength: 2,
maxLength: 10
}),
lastName: ko.observable().extend({
required: true
}),
tasks: ko.observableArray([]),
save: function() {
if (viewModel.errors().length == 0) {
ko.utils.postJson($("form")[0], this);
} else {
alert('Please check your submission.');
viewModel.errors.showAllMessages();
}
}
};
viewModel.errors = ko.validation.group(viewModel);
ko.applyBindings(viewModel);
this is going to my controller.
But I didnot get those parameters in my controller. Also I dont know the way to get these params in my controller.