How do I validate a form being filled out in Backbone? - javascript

So I have a new form set up it saves temporarily and all but I want it to only be able to update when it is validated otherwise show some errors. This is during the view section for the saveEdits event Any clue as to what I am doing wrong?
This is my main.js file
(function () {
window.App = {
Models: {},
Collections: {},
Views: {},
Templates: {},
Router: {}
};
// MODEL
App.Models.User = Backbone.Model.extend({
defaults: {
firstName: 'first',
lastName: 'last',
email: 'Email',
phone: '222',
birthday: '07/22/1980'
},
validate: function (attrs) {
if (!attrs.firstName) {
return 'You must enter a real first name.';
}
if (!attrs.lastName) {
return 'You must enter a real last name.';
}
if (attrs.email.length < 5) {
return 'You must enter a real email.';
}
if (attrs.phone.length < 10 && attrs.phone === int) {
return 'You must enter a real phone number, if you did please remove the dash and spaces.';
}
if (attrs.city.length < 2) {
return 'You must enter a real city.';
}
},
initialize: function() {
this.on('invalid', function (model, invalid) {
console.log(invalid);
});
}
});
//var userModel = new App.Models.User();
//VIEW
App.Views.User = Backbone.View.extend({
el: '#user',
//model: userModel,
//tagName: 'div',
//id: 'user',
//className: 'userProfile',
//template: _.template($("#userTemplate").html()),
//editTemplate: _.template($("#userEditTemplate").html()),
initialize: function (){
},
render: function() {
this.template = Handlebars.compile($("#userTemplate").html());
this.editTemplate = Handlebars.compile($("#userEditTemplate").html());
this.$el.html(this.template(this.model.toJSON()));
return this;
},
events: {
'click button.edit': 'editProfile',
'click button.save': 'saveEdits',
'click button.cancel': 'cancelEdits'
},
editProfile: function () {
this.$el.html(this.editTemplate(this.model.toJSON()));
},
saveEdits: function () {
var form = $(this.el).find('form#updateUser');
this.model.set({
firstName : form.find('.firstName').val(),
lastName : form.find('.lastName').val(),
email: form.find('.email').val(),
phone: form.find('.phone').val(),
birthday: form.find('.birthday').val()
});
this.model.validate();
this.render();
},
cancelEdits: function() {
this.render();
}
});
//start history service
Backbone.history.start();
var user = new App.Views.User({model: new App.Models.User()});
user.render();
})();
It works fine until I insert the this.model.validate and an error shows up stating this:
Uncaught TypeError: Cannot read property 'firstName' of undefined

You don't call validate explicitly -- it's meant called by the Backbone framework:
By default validate is called before save, but can also be called before set if {validate:true} is passed.
So to fix the code in the OP, use validate: true in the call to set:
this.model.set({
firstName : form.find('.firstName').val(),
// ...
}, { validate: true });
Note that, if you wanted to call validate, then you have to pass it the attrs parameter, as in this.model.validate(this.model.toJSON());

Related

Set model attribute during validation in Backbone

I have a model as
var Info = Backbone.Model.extend({
url: '',
defaults: {
username: '',
email: '',
error: ''
},
initialize: function(){
console.log('Obj created');
},
validate: function(attr){
if(!attr.username){
attr.error = 'Username cannot be empty';
}
}
});
View
var model = new Info();
var View1 = Backbone.View.extend({
model: model,
initialize: function(){
this.render();
},
render: function(){
var template = _.template($('#App1').html());
this.$el.html(template);
},
'events': {
'click #btn1': 'submit'
},
submit: function(){
var obj = {
username: $('#username').val(),
email: $('#pwd').val)
};
this.model.save();
}
});
Here I want to set model.set({error: 'Message goes here'}) during validate event.
However, validate only returns true / false. Is there a way we can set the value in the validate method.
You can set an error property inside validate() function:
this.set('error', 'Message goes here')
Once set, error message will be accessible via this.model.get('error') from within view object code.

Backbone model is not rendering default? Need Jade and Backbone gurus

Hey so I am having a problem where my templates change but the backbone model does not seem to provide the info I need. I am trying to solve this so I can move on to making collections. I am not getting any errors and I am using handlebars but have tried underscore's templating as well and the problem doesn't lie there but against the info displaying. When I click edit and the edit template shows it is showing undefined for #{firstName} and the others.
Main.js file
(function () {
window.App = {
Models: {},
Collections: {},
Views: {},
Templates: {},
Router: {}
};
// MODEL
App.Models.User = Backbone.Model.extend({
defaults: {
firstName: 'first',
lastName: 'last',
email: 'Email',
phone: '222',
birthday: 'date'
},
validate: function (attrs) {
if (!attrs.firstName) {
return 'You must enter a real first name.';
}
if (!attrs.lastName) {
return 'You must enter a real last name.';
}
if (attrs.email.length < 5) {
return 'You must enter a real email.';
}
if (attrs.phone.length < 10 && attrs.phone === int) {
return 'You must enter a real phone number, if you did please remove the dash and spaces.';
}
if (attrs.city.length < 2) {
return 'You must enter a real city.';
}
},
initialize: function() {
this.on('invalid', function (model, invalid) {
console.log(invalid);
});
}
});
//VIEW
App.Views.User = Backbone.View.extend({
el: '#user',
//model: userModel,
//tagName: 'div',
//id: 'user',
//className: 'userProfile',
//template: _.template($("#userTemplate").html()),
//editTemplate: _.template($("#userEditTemplate").html()),
initialize: function (){
},
render: function() {
this.template = Handlebars.compile($("#userTemplate").html());
this.editTemplate = Handlebars.compile($("#userEditTemplate").html());
this.$el.html(this.template(this.model.toJSON()));
return this;
},
events: {
'click button.edit': 'editProfile',
// 'click button.save': 'saveEdits',
'click button.cancel': 'cancelEdits'
},
editProfile: function () {
this.$el.html(this.editTemplate(this.model.toJSON()));
},
//saveEdits: function (e) {
// e.preventDefault();
// var formData = {},
// prev = this.model.previousAttributes();
//
//get form data
// $(e.target).closest('form').find(':input').not('button').each(function(){
// var el = $(this);
// formData[el.attr('class')] = el.val();
// });
//update model
// this.model.set(formData);
//render view
// this.render();
//update array
//},
cancelEdits: function() {
this.render();
}
});
//start history service
Backbone.history.start();
var user = new App.Views.User({model: new App.Models.User()});
user.render();
})();
Jade file
extends layout
block content
div.centerContent
h4 User goes here with equal before it no space
div#user
p #{firstName} #{lastName}
p #{email}
p #{phone}
p #{birthday}
button.edit Edit
script(id="userTemplate", type ="text/template")
p #{firstName} #{lastName} 1
p #{email} 2
p #{phone} 3
p #{birthday} 4
button.edit Edit
script(id="userEditTemplate", type ="text/template")
div
form(action="#")
input(type="text", class="firstName", value='#{firstName}')
input(type="text", class="lastName", value='#{lastName}')
input(type="email", class="email", value='#{email}')
input(type="number", class="phone", value='#{phone}')
input(type="date", class="birthday", value='#{birthday}')
button.save Save
button.cancel Cancel
hr
script(type="text/javascript", src="/js/main.js")
Try removing this line from your view
model: App.Models.User,
You are trying to assign a Backbone function directly to your view.
Also the problem is because of this line in your render method..
render: function() {
var template = Handlebars.compile($("#userTemplate").html());
var editTemplate = Handlebars.compile($("#userEditTemplate").html());
The editemplate is local to the render method and not available in other methods.
editProfile: function () {
this.$el.html(this.editTemplate(this.model.toJSON()));
},
You are trying to access the template using this.editTemplate , but the variable is local to the render method and not to the view.. So you are not able to access it..
Instead of creating it in the local scope, instantiate to the view it self so that it is available in other methods of the view.
render: function() {
this.template = Handlebars.compile($("#userTemplate").html());
this.editTemplate = Handlebars.compile($("#userEditTemplate").html());
Then it should be available to the other methods as well in the same view.

Backbone.js model.isValid() method not returning false for an invalid model

so I am trying to get the validate from my model to actually disable the save button but to re-validate when new input is included. Anyone know the best way to attempt this. Thanks! The problem I have with my method is that once it is disabled it doesn't return in state.
Here is the main.js file
(function () {
window.App = {
Models: {},
Collections: {},
Views: {},
Templates: {},
Router: {}
};
// MODEL
App.Models.User = Backbone.Model.extend({
defaults: {
firstName: 'first',
lastName: 'last',
email: 'Email',
phone: '222',
birthday: '07/22/1980'
},
validate: function (attrs) {
if (!attrs.firstName) {
return 'You must enter a real first name.';
}
if (!attrs.lastName) {
return 'You must enter a real last name.';
}
if (attrs.email.length < 5) {
return 'You must enter a real email.';
}
if (attrs.phone.toString().length < 10 ) {
//&& attrs.phone === int
return 'You must enter a real phone number, if you did please remove the dash and spaces.';
}
// if (attrs.birthday.length < 2) {
// return 'You must enter a real city.';
//}
},
initialize: function() {
this.on('invalid', function (model, invalid) {
console.log(invalid);
alert(invalid);
});
}
});
//var userModel = new App.Models.User();
//VIEW
App.Views.User = Backbone.View.extend({
el: '#user',
//model: userModel,
//tagName: 'div',
//id: 'user',
//className: 'userProfile',
//template: _.template($("#userTemplate").html()),
//editTemplate: _.template($("#userEditTemplate").html()),
initialize: function (){
},
render: function() {
this.template = Handlebars.compile($("#userTemplate").html());
this.editTemplate = Handlebars.compile($("#userEditTemplate").html());
this.$el.html(this.template(this.model.toJSON()));
return this;
},
events: {
'click button.edit': 'editProfile',
'click input.save': 'saveEdits',
'click button.cancel': 'cancelEdits'
},
editProfile: function () {
this.$el.html(this.editTemplate(this.model.toJSON()));
},
saveEdits: function () {
var form = $(this.el).find('form#updateUser');
this.model.set({
firstName : form.find('.firstName').val(),
lastName : form.find('.lastName').val(),
email: form.find('.email').val(),
phone: form.find('.phone').val(),
birthday: form.find('.birthday').val()
}, {validate: true} );
if(!this.model.isValid()) {
console.log('run');
$('.save').attr("disabled", "disabled");
} else {
console.log('run2');
alert('Changes have been made.');
$('.save').removeAttr("disabled");
return this.render();
}
},
},
cancelEdits: function() {
this.render();
}
});
//start history service
Backbone.history.start();
var user = new App.Views.User({model: new App.Models.User()});
user.render();
})();
And here is the Jade File:
extends layout
block content
div.centerContent
h4 User goes here with equal before it no space
div#user
p #{firstName} #{lastName}
p #{email}
p #{phone}
p #{birthday}
button.edit Edit
script(id="userTemplate", type ="text/template")
p {{firstName}} {{lastName}} 1
p {{email}} 2
p {{phone}} 3
p {{birthday}} 4
button.edit Edit
script(id="userEditTemplate", type ="text/template")
div
form(action="#")#updateUser
input(type="text", class="firstName", value='{{firstName}}')
input(type="text", class="lastName", value='{{lastName}}')
input(type="email", class="email", value='{{email}}')
input(type="number", class="phone",, value='{{phone}}')
input(type="date", class="birthday", value='{{birthday}}')
input(type="submit", value="Save").save Save
button.cancel Cancel
hr
script(type="text/javascript", src="/js/main.js")
The problem here is that you're calling the { validate: true } option to your model.set method, then you're subsequently calling model.isValid().
When you call model.set with the validate option set to true, Backbone.js will not set the properties you pass unless they all pass validation. So, by the time you call model.isValid() the model has been changed back to the previous version (before the .set call). model.isValid() automatically calls the model.validate() method and passes the current attributes of the model to it.
In your example, the values being passed to validate by the isValid method are the current (valid) attributes of your model. Because of this, isValid() is always going to validate to true. Which will cause you to never reach your else clause.
The solution here is instead of calling isValid to see if your model is valid (after passing validate: true to the set method) check the value of model.validationError. If model.validationError is a truthy value, then you know your model is invalid.
Here's a JSFiddle with an example of how to do that, with some documentation.

Getting a type error on my Backbone file?

so I will include the error along with my main.js file, if anyone can tell me what it is refering to? I think it is the templates but I cannot figure out what I am doing wrong.
Main.js file:
(function(){
//app can be the name of the project/app
window.App = {
Models: {},
Collections: {},
Views: {},
Templates: {},
Routes: {}
};
window.template = function (id) {
return _.template($('#' + id).html());
};
//Can get rid of the Collection and views out of the names of each
//User Model
App.Models.User = Backbone.Model.extend({
defaults: {
firstName: 'J.R.',
lastName: 'Smith',
email: 'jsmith#knicks.com',
phone: '212-424-6234',
birthday: '03/05/1982',
city: 'New York'
},
location: function(){
return this.get('firstName') + ' ' + this.get('lastName') + 'is currently in ' + this.get('city') + '.';
},
validate: function(attrs) {
if(!attrs.firstName) {
return 'You must enter a real name.';
}
if(!attrs.lastName) {
return 'You must enter a real name.';
}
if(attrs.email.length < 5 ) {
return 'You must enter a real email.';
}
if(attrs.phone.length < 10 && attrs.phone === int) {
return 'You must enter a real phone number, if you did please remove the dash and spaces.';
}
if(attrs.city.length < 2 ) {
return 'You must enter a real city.';
}
},
initialize: function(){
this.on('invalid', function(model, error){
console.log(error);
//when setting a user user.set('age', -55, {validate : true}); the validate true makes sure it validates
});
}
});
// list of users
App.Collections.UsersCollection = Backbone.Collection.extend({
model: App.Models.User
});
//User View
App.Views.UserView = Backbone.View.extend({
tagName: 'li',
events: {
'click .edit': 'edit'
},
edit: function() {
},
template: template('userTemplate'),
initialize: function() {
console.log("Render");
this.render();
},
render: function() {
var template = this.template(this.model.toJSON());
this.$el.html(template);
return this;
//always return this on render methods
}
});
// view for users
App.Views.UsersView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
},
render: function() {
this.collection.each(function(user) {
//user is the model associated to the new created user
var userView = new App.Views.UserView({model: user});
this.$el.append(userView.el);
}, this);
return this;
}
});
/*var user = new App.Collections.UsersCollection( );
var usersView = new App.Views.UsersView( users );
$( document.body ).append( usersView.render().el );
*/
})();
This is the jade file:
extends layout
block content
h1= title
p Welcome to #{title}
script(src='/js/main.js', type='text/javascript')
script(id='userTemplate', type='text/template')
<%=firstName%>
button.edit Edit
<%=lastName%>
button.edit Edit
<%=email%>
button.edit Edit
<%=phone%>
button.edit Edit
<%=birthday%>
button.edit Edit
<%=city%>
button.edit Edit
I don't think you are passing the JSON model to the template function.
Have you tried amending the code as follows:
Global templating function
window.template = function (id, model) {
return _.template($('#' + id).html(), model);
};
In your UserView
template: function() {
template('userTemplate', this.model.toJSON());
}
render: function() {
var template = this.template();
this.$el.html(template);
return this;
//always return this on render methods
}

why wont validate fire the error - backbone.js

Why won't validate fire the error as "fred" should make the validate condition return true when it is set?
Person = Backbone.Model.extend({
initialize: function () {
console.log('inisialize Person');
this.bind("change:name", function () {
console.log(this.get('name') + ' is now the name value')
});
this.bind("error", function (model, error) {
console.log(error);
});
},
defaults: {
name: '',
height: ''
},
validate: function (attributes, options) {
if (attributes.name == "fred") { //why wont this work?
return "oh no fred is not allowed";
}
}
});
//var person = new Person({ name: 'joe', height: '6 feet' });
var person = new Person();
person.set({ name: 'fred', height: '200' });
Your validate() is called when saving, but not when setting an attribute unless you explicitly tell it to do so. From the docs:
By default validate is called before save, but can also be called
before set if {validate:true} is passed.
Try this: In the initialize(), change the this.bind('error') to this.on('invalid') The 'error' event is for failures at the server, after when calling save(). The 'invalid' is for validation errors on the client side. Lastly, add {validate: true} as a second argument to the person.set() calls. Backbone doesn't validate set() by default.
Person = Backbone.Model.extend({
defaults: {
name: '',
height: ''
},
validate: function(attributes) {
if(attributes.name === 'fred' )
return 'oh no fred is not allowed';
},
initialize: function() {
alert('welcome');
this.on('invalid', function(model, error){
alert(error);
});
//listeners
this.on('change:name', function(model) {
var name = model.get('name');
alert('changed ' + name);
});
});
var person = new Person();
person.set({ name: 'fred', height: '200' }, {validate: true}); //oh no fred is not allowed

Categories

Resources