Backbone model is kept from changing - javascript

My app uses Backbone. It has a model that users can change. This model must be validated before being changed. If it has changed, a function is called. When users click on save, the model is saved if and only if it has changed.
My problem is that when the change event is fired the model has no longer changed, therefore it will not be saved.
Here is the code: http://jsfiddle.net/keepyourweb/jQL8V/
var model = Backbone.Model.extend({
initialize: function() {
},
default: {
'first_name': 'none',
'last_name': 'none'
},
validate: function(attr) {
if (_.isEmpty(attr['first_name'])) return 'Error name required';
}
});
var test = new model,
showError = function(model, error) {
alert(error);
},
changed = function() {
alert('changed!');
};
test.bind('change', changed);
test.set({'first_name': 'test_name', 'last_name': 'test_surname'}, {error: showError});
$('#save').bind('click', function() {
if (test.hasChanged()) alert('Saved!!');
});

Related

Uncaught TypeError: Cannot read property 'el' of undefined, Only first model's description is visible. I want to show all the models

This is TodoView. I have added return this in render function of TodoView
var TodoView=Backbone.View.extend({
el:'h3',
tagName:'article',
id:'todo-view',
className:'todo',
template:_.template('<h3 class="<%=status%>">'+'<input type=checkbox '+'<%if(status==="complete") print("checked")%>/>'+'<%=description %></h3>'),
events:{
"click h3":"alertStatus",
"change input":"toggleStatus"
},
alertStatus:function(e)
{
console.log('Hey you clicked TodoItem !');
},
toggleStatus:function()
{
this.model.toggleStatus();
},
initialize:function()
{
this.model.on('change',this.render,this);
this.model.on('destroy',this.remove,this);
},
render:function()
{
var attributes=this.model.toJSON();
this.$el.html(this.template(attributes));
this.$el.delegate('h3','click',this.alertStatus);
return this;
},
remove:function()
{
this.$el.remove();
}
});
This is view of the collection.
var TodoListView=Backbone.View.extend({
el:'h3',
Here forEach is not fetching all the models. Only first model is visible in div. I want to show all the models in the view.
render:function()
{
this.collection.forEach(this.addOne,this);
return this;
},
In this addOne function, The models are logging out on console if i use $el.html(), but models are not visible on the view; (on the browser)
addOne:function(todoItem)
{
var todoView=new TodoView({model:todoItem});
this.$el.append(todoView.render().$el.html());
},
initialize:function()
{
this.collection.on('change',this.addOne,this);
this.collection.on('add',this.addOne,this);
}
});
var todoListView=new TodoListView({collection:todoList});
todoListView.render();
console.log(todoListView.el);

How can I prevent Backbones save method from trying to update every model?

I am creating a crud web app with backbone. I am writing the functionality to update a resource (PUT). I am trying to achieve this by fetching a models properties from the server (see the SubscriberView) and on successfully fetching the resource to instantiate a SubscriberEditView whereby the newly fetched model is passed.
So far this works as expected; SubscriberEditView renders an html form which is populated with the model instance properties.
When I enter a new login value into the form I can trigger the update function which successfully makes a PUT request to the server resource and updates the model instance as expected.
However, the problem is that when I then repeat this process with another model instance the PUT request is made against the curent model AND the previously instantiated model.
Is the reason for this because I now have two instances of SubscriberEditView? Or is it something else that I have missed/misunderstood.
Please see below the described code.
// The view for a single subscriber
var SubscriberView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#subscribers-tmpl').html()),
initialize: function() {
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
var html = this.template(this.model.toJSON());
this.$el.html(html);
return this;
},
events: {
'click .remove': 'onRemove',
'click .edit-subscriber': 'editSubscriber',
},
editSubscriber: function() {
var getSubscriberModel = this.model.set('id', this.model.attributes.id, {silent:true})
getSubscriberModel.fetch({
success: function (model, response) {
$('#addSubscriber').fadeOut();
new SubscriberEditView({model:model});
},
error: function (response) {
console.log('There was an error');
}
});
},
onRemove: function() {
this.model.destroy();
}
});
// The edit view
var SubscriberEditView = Backbone.View.extend({
tagName: 'div',
el: '#updateSubscriber',
template: _.template($('#subscriberEdit-tmpl').html()),
initialize: function() {
this.model.on('sync', this.render, this);
},
events: {
'click #close': 'cancel',
'click .save-subscriber': 'update'
},
update: function() {
var $login = this.$('#login');
this.model.save({
login: $login.val(),
},
{
dataType: 'text',
success: function (model, response, options) {
console.log('success');
},
error: function (model, response, options) {
console.log('error');
}
});
},
cancel: function() {
$('#addSubscriber').fadeIn();
$('#editInner').fadeOut();
},
render: function() {
var html = this.template(this.model.toJSON());
this.$el.html(html);
},
});
If anyone could help then that would be greatly appreciated.
Cheers
The issue is el: '#updateSubscriber',. All your view instances are pointing to same element to which events are delegated. So clicking on any of the .save-subscriber will trigger update for all the view instances. You should not specify el for a view that is going to have more than one instance.

I am unable to execute validation in Backbone.js.

Whenever I set the age attribute to negative value it doesn't return false.
I have also tried executing this code in the console and still nothing happens
<script>
var Human = Backbone.Model.extend({
// If you return a string from the validate function,
// Backbone will throw an error
defaults: {
name: 'Guest user',
age: 23,
occupation: 'worker'
},
validate: function( attributes ){
if( attributes.age < 0){
return "Age must me positive";
}
if( !attributes.name ){
return 'Every person must have a name';
}
},
work: function(){
return this.get('name') + ' is working';
}
});
var human = new Human;
human.set("age", -10);
human.on('error', function(model, error){
console.log(error);
});
</script>
There are a few things wrong with your code:
The event for validation is invalid, error is for ajax requests.
Validation on set doesn't happen by default, you need to pass { validate: true } as an option.
You are listening to the event AFTER setting, so it won't get called for that set.
i.e:
human.on('invalid', function(model, error) {
console.log(error);
});
human.set("age", -10, { validate: true });

Backbone collection fetch not firing

I'm new to backbone and I'm trying to send and receive data from the server in Json format. It just won't work. Here's my code (BTW, I'm using backbone aura):
Collection
define(['sandbox', '../models/message'], function(sandbox, Message) {
'use strict';
var Messages = sandbox.mvc.Collection({
model: Message,
url: '/messagelist.php',
localStorage: new sandbox.data.Store('messages-backbone-require'),
parse: function(response){
return response.rows;
}
});
return Messages;
});
Model
define(['sandbox'], function(sandbox) {
'use strict';
var Message = sandbox.mvc.Model({
defaults: {
opened: '',
messageid: '',
phonenumber: '',
numbername: '',
text: ''
},
parse: function(data){
return data;
}
});
return Message;
});
View
define(['sandbox', '../models/message', 'text!../templates/incoming_messages.html'], function(sandbox, Message, incomingMessagesTemplate) {
'use strict';
var AppView = sandbox.mvc.View({
widgetTemplate: sandbox.template.parse(incomingMessagesTemplate),
events: {
'click .refresh': 'refresh'
},
initialize: function() {
this.$el.html(this.widgetTemplate);
sandbox.events.bindAll(this);
this.collection.bind('createMessageList', this.createMessageList);
},
createMessageList: function() {
// Will work with the received data here
},
render: function() {
var handle = 'h4';
this.$el.draggable({handle: handle});
this.createMessageList();
},
refresh: function() {
this.createMessageList();
}
});
return AppView;
});
Main
define(['sandbox', './views/app', './collections/messages'], function(sandbox, AppView, Messages) {
'use strict';
return function(options) {
var messages = new Messages();
new AppView({
el: sandbox.dom.find(options.element),
collection: messages
}).render();
messages.fetch({
data: {
type: 'incoming',
offset: 0,
offsetcount: 25
},
type: 'GET',
success: function() {
console.log(messages.models); // Shows an empty array.
}
});
};
});
I've check logs and it seems that the ajax request (collection.fetch()) is not firing or is not able to communicate with the server. How can I fix this?
The problem is with the Backbone.LocalStorage plugin. When you assign Collection.localStorage, the plugin takes over the fetch command and reads the data from local storage instead of the server.
See my answer in this SO question on some options on how to solve this.

Updating Model from form in Backbone

I am creating a simple application using Backbone and Laravel to manage bookings, and I'm working on a simple form to update user data for the currently signed in user.
I was wondering, is there a better, more efficient way than the way I have done, to update your model with the input data from the form?
I have created a method called update in the model, which is passed a DOM object of the form. I assume this isn't the best way to go about it.
Any help would be very much appreciated!
var Account = Backbone.Model.extend({
url: "/settings/account",
initialize: function()
{
},
update: function(form)
{
this.set({
first_name : form.find('#first-name').val(),
last_name : form.find('#last-name').val(),
email : form.find('#email').val(),
landline: form.find('#landline').val(),
mobile: form.find('#mobile').val()
});
return this.save();
}
});
var user = new Account;
var AccountView = Backbone.View.extend({
el: $("div.section"),
template: _.template($("#account-template").html()),
events: {
"submit #account": "update"
},
initialize: function()
{
_.bindAll(this, 'render', 'update');
this.render();
},
render: function()
{
$(this.el).html(this.template(this.model.toJSON()));
},
update: function()
{
var form = $(this.el).find('form#account');
user.update(form);
$(this.el).find('.alert-success').show();
return false;
}
});
var Router = Backbone.Router.extend({
routes: {
"account": "account"
},
account: function()
{
user.fetch({
success: function()
{
return new AccountView({model:user});
}
});
}
});
It is considered bad design when the model is aware of its view. I would make the update of the model orchestrated by the view.
I think the easiest way would be to do this in the view:
update: function()
{
var form = $(this.el).find('form#account');
user.set({
first_name : form.find('#first-name').val(),
last_name : form.find('#last-name').val(),
email : form.find('#email').val(),
landline: form.find('#landline').val(),
mobile: form.find('#mobile').val()
});
user.save();
$(this.el).find('.alert-success').show();
return false;
}
Otherwise the model would have to know where to retrieve the values. Also, when you someday use a different view you would also have to change the model class.

Categories