I'm trying to create very simple login with backbonejs. Collection stores usernames and passwords. Login view has two inputs and on click it should perform check function and compare input value with data from collection.
Html part looks like this:
<div class="login-block">
<script type="text/template" id="start">
<form id="login">
<div class="input-wrapper"><input type="text" placeholder="Username" id="username" required></div>
<div class="input-wrapper"><input type="password" placeholder="Password" id="password" required></div>
<div class="input-wrapper"><button class="btn">Sign in!</button></div>
</form>
</script>
<div class="error" class="block">
Error
</div>
<div class="success">
Success
</div>
</div>
Here is my Js code:
var User = Backbone.Model.extend({
defaults: {
login: 'root',
mail: 'root#mail.com',
password: ''
}
});
var user = new User();
//variable to store username
var loginData = {
username: "",
password: ""
}
// userbase
var UserCollection = Backbone.Collection.extend({
model: User
});
var userCollection = new UserCollection([
{
username: 'Ivan',
mail: 'ivan#mail.com',
password: '1234'
},
{
username: 'test',
mail: 'test#mail.com',
password: 'test'
}
]);
// login page
var LoginView = Backbone.View.extend({
el: $(".login-block"),
events: {
"click .btn": "check"
},
check: function(){
loginData.username = this.$el.find("#username").val(); // store username
loginData.password = this.$el.find("#password").val();// store password
if (loginData.username === userCollection.each.get("username") && loginData.password === userCollection.each.get("password"))
{appRouter.navigate("success", {trigger: true});
}else{
appRouter.navigate("error", {trigger: true});
}
},
render: function () {
//$(this.el).html(this.template());
var template = _.template($('#start').html())
$(this.el).html(template());
//template: template('start');
return this;
}
});
var loginView = new LoginView({collection: userCollection});
var AppRouter = Backbone.Router.extend({
routes: {
'': 'index', // start page
'/error': 'error',
'/success': 'success'
},
index: function() {
loginView.render();
console.log("index loaded");
},
error: function(){
alert ('error');
},
success: function(){
console.log('success');
}
});
var appRouter = new AppRouter();
Backbone.history.start();
It works fine to the check function, and it stores username and password, but something is clearly wrong either with router or check function when it starts comparison. Instead of routing to success or error page, it rerenders index page.
P.S I didn't use namespacing and code in general is not of a greatest quality, but it was made for educational purpose only.
You have to add the attribute type="button" to your button, otherwise it will submit the form when clicked (See this question):
<script type="text/template" id="start">
<form id="login">
<div class="input-wrapper"><input type="text" placeholder="Username" id="username" required></div>
<div class="input-wrapper"><input type="password" placeholder="Password" id="password" required></div>
<div class="input-wrapper"><button class="btn" type="button">Sign in!</button></div>
</form>
</script>
You can also return false in the click event handler, which would cancel the default action. (submitting the form, if you don't add type="button").
For comparing the values with the hardcoded collection, you can't call each as you where doing (which is an iteration function provided by Underscore) because you would receive an error. You could use Underscore's findWhere method which is also available in Backbone collections. So the click event handler (Your check function) could look like this:
check: function(){
loginData.username = this.$el.find("#username").val(); // store username
loginData.password = this.$el.find("#password").val();// store password
if(userCollection.findWhere({username: loginData.username, password: loginData.password})){
appRouter.navigate("success", {trigger: true});
}else{
appRouter.navigate("error", {trigger: true});
}
return false;
},
You can try it on this fiddle
The logic check you're doing doesn't look like it would work to me. I would expect the following to generate an error:
userCollection.each.get('username')
the function you're calling on your collection, each, is a wrapped underscore method which takes a function callback as a parameter. If you want to check your username and password, I'd do something like this:
var user = userCollection.findWhere({ username: loginData.userName });
This will return you the model where the username matches. Then you can check the password of that model:
if (user.get('password') === loginData.password) {
// do something
} else {
// do something else
}
EDIT Heck, you can do both checks at once:
var user = userCollection.findWhere({ username: loginData.userName, password: loginData.password });
I'll leave the previous code up just to demonstrate.
Related
I'm new to Backbone.js, and just finished running through a basic tutorial to create a "user list" system (https://www.youtube.com/watch?v=FZSjvWtUxYk) where all the templates, scripts, etc are created inline. I got everything working pretty easily, so I decided to try and modularize things since I know that's the best practice. I'm following this guide to the AMD methodology (https://cdnjs.com/libraries/backbone.js/tutorials/organizing-backbone-using-modules) and have everything working properly except for one thing - when editing a user, the "current" data isn't being loaded into the form. All of the issues I've found on SO and other places so far have been solved by putting the template generating code inside the success: callback of the .fetch() call, but I'm already doing that.
Here's the code:
(I'm leaving out the main.js and app.js that handle the require.js configuration, router init, etc. They seem to be working just fine.)
// Filename: router.js
define([
'jquery',
'underscore',
'backbone',
'views/userList',
'views/editUser'
], function($, _, Backbone, UserListView, EditUserView){
var AppRouter = Backbone.Router.extend({
routes: {
'': 'home',
'new': 'editUser',
'edit/:id': 'editUser'
}
});
var initialize = function(){
var app_router = new AppRouter;
app_router.on('route:home', function(){
var userListView = new UserListView();
userListView.render();
});
app_router.on('route:editUser', function(id) {
var editUserView = new EditUserView();
editUserView.render({ id: id });
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
views/editUser.js
// Filename: views/editUser
define([
'jquery',
'underscore',
'backbone',
'models/user',
'text!/templates/editUser.html'
], function($, _, Backbone, UserModel, rawEditUserTemplate) {
var userListView = Backbone.View.extend({
// Element to use for this view
el: $('.page'),
// Function to call when this view is rendered
render: function(options) {
var that = this;
// If there is an ID, we are editing
if ( options.id ) {
// Create the user, passing the ID
that.editUser = new UserModel({ id: options.id });
// Fetch the user data
that.editUser.fetch({
// When the fetch is returned
success: function(userData) {
// Generate the template and pass the data in
var editUserTemplate = _.template( rawEditUserTemplate );
that.$el.html(editUserTemplate({ user: userData }));
}
})
}
else { // We are creating a new user
// Generate the template with an empty user
var editUserTemplate = _.template( rawEditUserTemplate );
this.$el.html(editUserTemplate({ user: null }));
}
},
events: {
'submit .edit-user-form': 'saveUser',
'click .delete': 'deleteUser'
},
saveUser: function(e) {
e.preventDefault();
// Get the details
var userDetails = $(e.currentTarget).serializeObject();
// Create a user model
var user = new UserModel();
// Save the user details
user.save(userDetails, {
success: function(user) {
Backbone.history.navigate('', { trigger: true });
}
});
},
deleteUser: function(e) {
e.preventDefault();
// Destroy the user we are editing
this.editUser.destroy({
// When the destroy is finished
success: function() {
// Back to home
Backbone.history.navigate('', { trigger: true });
}
});
}
});
// Our module now returns our view
return userListView;
});
templates/editUser.html
<form class="edit-user-form">
<legend><%= user ? 'Update' : 'Create' %> User</legend>
<div class="form-group">
<label for="firstname">First Name</label>
<input type="text" class="form-control" name="firstname" id="firstname" value="<%= user ? user.get('firstname') : '' %>" />
</div>
<div class="form-group">
<label for="lastname">Last Name</label>
<input type="text" class="form-control" name="lastname" id="lastname" value="<%= user ? user.get('lastname') : '' %>" />
</div>
<div class="form-group">
<label for="age">Age</label>
<input type="text" class="form-control" name="age" id="age" value="<%= user ? user.get('age') : '' %>" />
</div>
<hr />
<button class="btn btn-success" type="submit"><%= user ? 'Update' : 'Create' %></button>
<% if ( user ) { %>
<input type="hidden" name="id" id="id" value="<%= user.id %>" />
<button class="btn btn-danger delete">Delete</button>
<% }; %>
</form>
Using this code, I get a blank edit form regardless of whether or not I'm editing or creating, HOWEVER the "Create" vs "Update" text switch in the template is working properly. This means that a user object is in fact being passed, and when I add a console.log(user) into the template file, it is in fact showing me user data. When I log user.get('firstname') or any other attribute, however, it logs "undefined".
The issue was in my User model, which I didn't include above because I didn't understand at the time why it could be relevant.
I was defining it as:
var userModel = Backbone.Model.extend({
url: '/users'
});
When it should have been:
var userModel = Backbone.Model.extend({
urlRoot: '/users'
});
The wrong option was causing the API to return a collection rather than a model, so the .get() wasn't able to work properly.
I am using MeteorJS to develop simple web app. I am new at MeteorJs. I use useraccounts:core package and meteor-roles by alanning. Is it posible to assign role to user while registering a new user ?
EDIT 1
I have tried using onCreateUser hook but something is not working.
Accounts.onCreateUser(function(options, user){
var role = ['unselected'];
Roles.addUsersToRoles(user, role);
return user;
});
The below method must run on the server side only. The clue is that you need to create first the user, get the id from creation and then attach a role to it.
Meteor.methods({
rolesCreateUser: function (user) {
if (_.isObject(user)) {
if (user.username) {
var id = Accounts.createUser({
username: user.username,
email: user.email,
password: user.password
});
//We add roles to the user
if (user.roles.length > 0) {
Roles.addUsersToRoles(id, user.roles);
}
_.extend(user, {id: id});
return user;
}
}
}
});
And then on the client side call the method with user's data:
Meteor.call('rolesCreateUser', newUserData, function (error, newCreatedUser) {
if (error) {
//The error code
} else {
//Do something with newCreatedUser
}
});
I have this way to create users (if i understond you example you alredy have some users, just need some roles, so with the current user just create this), also use some Login buttons customized or something like that
Server.js
Meteor.methods({
createUsers: function(email,password,roles,name){
var users = [{name:name,email:email,roles:[roles]},
];
.each(users, function (user) {
var id;
id = Accounts.createUser({
email: user.email,
password: password,
profile: { name: user.name }
});
if (user.roles.length > 0) {
Roles.addUsersToRoles(id, user.roles);
}
});
},
deleteUser : function(id){ ///Some Delete Method (ignore if dont needed)
return Meteor.users.remove(id);
},
});
Publish Methods
//publish roles
Meteor.publish(null, function (){
return Meteor.roles.find({})
})
Meteor.publish("Super-Admin", function () {
var user = Meteor.users.findOne({_id:this.userId});
if (Roles.userIsInRole(user, ["Super-Admin"])) {
return Meteor.users.find({}, {fields: {emails: 1, profile: 1, roles: 1}});
}
this.stop();
return;
});
Meteor.publish("Admin", function () {
var user = Meteor.users.findOne({_id:this.userId});
if (Roles.userIsInRole(user, ["Admin"])) {
return Meteor.users.find({}, {fields: {emails: 1, profile: 1, roles: 1}});
}
this.stop();
return;
});
Meteor.publish(null, function (){
return Meteor.roles.find({})
})
So on the Client side client/register.html
<template name="register">
<form id="register-form" action="action" >
<input type="email" id="register-email" placeholder="Nombre Nuevo Usuario">
<input type="password" id="register-password" placeholder="Password">
<select id="register-rol" class="form-control">
<option value="Admin" selected>Admin</option>
<option value="Super-Admin" selected>Super Admin</option>
<option value="Normal" selected>Normal</option>
</select>
<input type="submit" value="Register">
</form>
</tempalate>
and on register.js call the server methods
Template.registrar.events({
'submit #register-form' : function(e, t) {
e.preventDefault();
var email = t.find('#register-email').value,
password = t.find('#register-password').value,
roles = $( "#register-rol" ).val();
Meteor.call("createUsers", email, password,roles);
return false;
},
'click #deleteUser' : function(event,template){
var idUsuario= this._id;
Meteor.call('deleteUser',{_id:idUsuario})
}
});
Delete Part(html) this is optional just too look if accounts are creating correctly
{{#each users}}
<li id="user"><h6>{{email}}</h6><h6>{{roles}}</h6></li>
<button id="deleteUser" class="btn btn-danger btn-xs" > Borrar Usuario {{email}} </button>
{{/each}}
client/registerList.js
Template.registrar.helpers({
users: function () {
return Meteor.users.find();
},
email: function () {
return this.emails[0].address;
},
roles: function () {
if (!this.roles) return '<none>';
return this.roles.join(',');
}
});
Remember Subscribe
Meteor.subscribe('Admin');
Meteor.subscribe('Super-Admin');
Hope this help sorry for the messy code
You may want to use OnCreateUser hook: http://docs.meteor.com/#/full/accounts_oncreateuser
Just getting started with Parse.com and following their JavaScript ToDo app tutorial and having some issues.
Basically it will work perfectly and post to the Parse database when I just have the Username and Password but if I try to add any additional fields like email or phone, it won't send it to the database. I have already defined both email and phone fields in my Parse database.
Any help would be appreciated. Thanks!
My html for my form:
<script type="text/template" id="login-template">
<header id="header"></header>
<form class="signup-form">
<h2>Sign Up</h2>
<div class="error" style="display:none"></div>
<input type="text" id="signup-username" placeholder="Username" />
<input type="password" id="signup-password" placeholder="Create a Password" />
<input type="email" id="signup-email" placeholder="Email" />
<input type="text" id="signup-phone" placeholder="Phone" />
<button>Sign Up</button>
</form>
</div>
</script>
My JS:
var LogInView = Parse.View.extend({
events: {
"submit form.signup-form": "signUp"
},
el: ".content",
initialize: function() {
_.bindAll(this, "signUp");
this.render();
},
signUp: function(e) {
var self = this;
var username = this.$("#signup-username").val();
var password = this.$("#signup-password").val();
var email = this.$("#signup-email").val();
var phone = this.$("#signup-phone").val();
Parse.User.signUp(username, password, email, phone, { ACL: new Parse.ACL() }, {
success: function(user) {
new ManageTodosView();
self.undelegateEvents();
delete self;
},
error: function(user, error) {
self.$(".signup-form .error").html(error.message).show();
self.$(".signup-form button").removeAttr("disabled");
}
});
this.$(".signup-form button").attr("disabled", "disabled");
return false;
},
render: function() {
this.$el.html(_.template($("#login-template").html()));
this.delegateEvents();
}
});
You should use the following syntax, like specified in the javascript docs (https://parse.com/docs/js_guide):
var user = new Parse.User();
user.set("username",username);
user.set("password",password);
user.set("email",email);
user.set("phone",phone);
user.signUp(null,{
success:function(user){...},
error:function(user,error){...}
});
You can add any fields you want with the set method.
I had to change <!--<button>Submit</button>--> to <!--<input type="submit"></input>--> to get this to work but then it did. Thanks.
I am trying to figure out how to create a form that has a input field, as well as a "+" button to add another input field. I than want to take those inputs and insert them into a collection. Than output them as a list. Basically I want to give the option to add as many list items as possible. If there is a better way to do this instead of a "+" button to add input fields,I' open to any suggestions.
code so far
HTML
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
<template name="postItem">
<h2>{{service}}</h2>
<ul><li>{{task}}</li></ul>
</template>
<template name="postSubmit">
<form>
<label for="service">Add a Service</label>
<input name="service" type="text" value="" placeholder="Service Type"/>
<label for="task">Add a task (spaces between each kind)</label>
<input name="task" type="text" value="" placeholder="type or task for service"/>
<input type="submit" value="Submit" />
</form>
</template>
JS
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
service: $(e.target).find('[name=service]').val(),
task: $(e.target).find('[name=task]').val()
};
Meteor.call('post', post, function(error, id) {
if (error)
return alert(error.reason);
Router.go('postPage', {_id: id});
});
}
});
Posts = new Meteor.Collection('posts');
Posts.allow({
update: ownsDocument,
remove: ownsDocument
});
Posts.deny({
update: function(userId, post, fieldNames) {
// may only edit the following two fields:
return (_.without(fieldNames, 'service', 'task').length > 0);
}
});
Meteor.methods({
post: function(postAttributes) {
var user = Meteor.user(),
postWithSameLink = Posts.findOne({url: postAttributes.url});
// ensure the user is logged in
if (!user)
throw new Meteor.Error(401, "You need to login to post new stories");
// ensure the post has a service
if (!postAttributes.service)
throw new Meteor.Error(422, 'Please fill in a service');
// ensure the post has a task
if (!postAttributes.task)
throw new Meteor.Error(422, 'Please fill in a task');
// check that there are no previous posts with the same link
if (postAttributes.url && postWithSameLink) {
throw new Meteor.Error(302,
'This link has already been posted',
postWithSameLink._id);
}
// pick out the whitelisted keys
var post = _.extend(_.pick(postAttributes, 'service', 'task'), {
userId: user._id,
author: user.username,
submitted: new Date().getTime()
});
var postId = Posts.insert(post);
return postId;
}
});
Template.postsList.helpers({
posts: function() {
return Posts.find({}, {fields: {service: 1, task: 1}}).map(function(post, index) {
return post;
});
}
});
I am fairly new to BackboneJS. After writing multiple GET implementation, I am trying to implement Login screen with Backbone JS.
Folder Structure
app
-->model
-->view
-->templates
-->server
formSignIn.html
<form class="form-signin" role="form">
<h2 class="form-signin-heading">Please sign in</h2>
<input type="email" id="email" class="form-control" placeholder="Email address" required="" autofocus="">
<input type="password" id="password" class="form-control" placeholder="Password" required="">
<label class="checkbox">
<input type="checkbox" value="remember-me"> Remember me
</label>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
Backbone View
var SignInView = Backbone.View.extend({
el:$('.container'),
template:_.template('../templates/formSignIn.html'),
events: {
"click .btn":"signIn"
},
initialize: function() {
this.model.on('change', this.render, this);
},
render: function() {
var attributes = this.model.toJSON();
this.$el.html(this.template(attributes));
},
signIn: function() {
this.model.signIn({
email: $('#email').val(),
password: $('#password').val()
});
}
});
var signInView = new SignInView({model: signInModel});
signInView.render();
Backbone Model
var SignInModel = Backbone.Model.extend({
url:function() {
'http://localhost:3000/singIn'
},
defaults: {
email:"",
password:""
},
parse: function(resp) {
return resp;
},
signIn: function() {
this.save();
}
});
var signInModel = new SignInModel();
Issues:
Template HTML is not rendering. When I open the page it shows ../templates/formSignIn.html. It means _template is not recognizing the html.
How is the view and model implementation? Is this the right way of doing? I am not very confident about calling the model's save().
In answer to your first question _.template(...) takes in a string. If you want the contents of ../templates/formSignIn.html you must include it in the dom or request it, for example using ajax.
If included in the dom it would look something it like this:
// Somewhere in the html...
<script type="text/html" id="form-signin-tpl">
<form class="form-signin" role="form">
...
</form>
</script>
// in your view
_.template($('#form-signin-tpl').html());
If you need to request the template during runtime you can use RequireJS which handles this nicely, or you could manually request it with jQuery, perhaps like this:
$.get( "path/to/templates/formSignIn.html", function( html ) {
var tpl = _.template(html);
});
In answer to the second question
the model's url parameter is a string, not a function.
You only need to define parse if you need to customize how the server's data is parsed.
This is probably more what you're going for:
var SignInModel = Backbone.Model.extend({
url: 'http://localhost:3000/singIn',
defaults: {
email:"",
password:""
},
signIn: function() {
this.save();
}
});
var signInModel = new SignInModel();
Lastly, regarding authenticating a user, a model might not be the best way to handle this. There are a few SO questions regarding athenticating a user in Backbone apps, such as this one