Reputation: 4080
I am rather new to backbone and wanted to test a simple script that handles a to do list. Here is the code i used so far:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template($('#' + id).html());
}
App.Models.Task = Backbone.Model.extend({
validate: function(attributes) {
if ( !$.trim(attributes.title) ) {
return 'Invalid title';
}
}
});
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task
});
App.Views.Task = Backbone.View.extend({
tagName: 'li',
template: template('taskTemplate'),
initialize: function () {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
events: {
'click .edit': 'editTask',
'click .delete': 'destroy'
},
destroy: function() {
if (confirm('Are you sure?')) {
this.model.destroy();
}
},
remove: function() {
this.$el.remove();
},
editTask: function() {
var newTaskTitle = prompt('New title:', this.model.get('title'));
this.model.set('title', newTaskTitle, {validate: true});
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
App.Views.AddTask = Backbone.View.extend({
el: 'form#addTask',
initialize: function() {
},
events: {
'submit': 'submit'
},
submit: function(event) {
event.preventDefault();
var newTaskTitle = $(event.currentTarget).find('input[type=text]').val();
var task = new App.Models.Task({ title: newTaskTitle });
this.collection.add(task, {add: true, merge: false, remove: false});
}
});
App.Views.Tasks = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.Task({ model: task });
this.$el.append(taskView.render().el);
}
});
var tasks = new App.Collections.Tasks([
{
title: 'Go to store',
priority: 4
},
{
title: 'Go to mall',
priority: 3
},
{
title: 'Get to work',
priority: 5
}
]);
var addTaskView = new App.Views.AddTask({ collection: tasks });
var tasksView = new App.Views.Tasks({ collection: tasks });
$('div.tasks').append(tasksView.render().el);
})();
So the model validation works fine ... the only pb is that collection.add does not validate the newly added model .... is the a way to force the validation?
Thanks, Rares
Upvotes: 2
Views: 4036
Reputation: 434685
From the fine manual:
validate
model.validate(attributes, options)
[...] By default validate is called before
save
, but can also be called beforeset
if{validate:true}
is passed.
Collection#add
does not call save
nor does it call set
with the validate: true
option. If you want to validate during add
, say so:
collection.add(models, { validate: true });
That will get validate:true
all that way down to Model#set
.
A quick look at a simplified example may be helpful:
var M = Backbone.Model.extend({
set: function() {
console.log('setting...');
Backbone.Model.prototype.set.apply(this, arguments);
},
validate: function() {
console.log('validating...');
return 'Never!';
}
});
var C = Backbone.Collection.extend({
model: M
});
var c = new C;
c.on('add', function() {
console.log('Added: ', arguments);
});
c.on('invalid', function() {
console.log('Error: ', arguments);
});
Now if we do this (http://jsfiddle.net/ambiguous/7NqPg/):
c.add(
{ where: 'is', pancakes: 'house?' },
{ validate: true }
);
You'll see that set
is called with validate: true
, validate
will be called, and you'll get an error. But if you say this (http://jsfiddle.net/ambiguous/7b2mn/):
c.add(
{ where: 'is', pancakes: 'house?' },
{add: true, merge: false, remove: false} // Your options
);
You'll see that set
is called without validate: true
, validate
will not be called, and the model will be added to the collection.
The above behavior is quite strongly implied but not explicitly specified so you may not want to trust it. Model#initialize
does say:
you can pass in the initial values of the attributes, which will be
set
on the model.
and set
does explicitly mention the validate
option. However, there is no guarantee that Collection#add
will send options to the model constructor or set
or that the model's constructor will send the options to set
. So if you want to be really paranoid and future proof, you could add a quick check for this "options get all the way down to set
" behavior to your test suite; then, if it changes you'll know about it and you can fix it.
Upvotes: 2
Reputation: 3727
if you pass options to your collection add method, the validation method will not be called and as your arguments in this case are all set to the default value, there is not need to pass them
this.collection.add(task);
you may want to take a look at this question. Prevent Backbone.js model from validating when first added to collection
Upvotes: 1