Reputation: 234
I am building a test application in Backbone.js (my first app using Backbone). The app goes like this:
My issue is with item 5. When I save a plan, I add the model to the collection then redirect to the initial view. At this point, I fetch data from the server. When I fetch data from the server, this overwrites my collection and my added model is gone.
How can I prevent this from happening? I have found a way to do this but it is definitely not the correct way at all. Below you will find my code examples for this. Thanks for the help.
PlansListView View:
var PlansListView = Backbone.View.extend({
tagName : 'ul',
initialize : function()
{
_.bindAll( this, 'render', 'close' );
//reset the view if the collection is reset
this.collection.bind( 'reset', this.render , this );
},
render : function()
{
_.each( this.collection.models, function( plan ){
$( this.el ).append( new PlansListItemView({ model: plan }).render().el );
}, this );
return this;
},
close : function()
{
$( this.el ).unbind();
$( this.el ).remove();
}
});//end
NewPlanView Save Method
var NewPlanView = Backbone.View.extend({
tagName : 'section',
template : _.template( $( '#plan-form-template' ).html() ),
events : {
'click button.save' : 'savePlan',
'click button.cancel' : 'cancel'
},
intialize: function()
{
_.bindAll( this, 'render', 'save', 'cancel' );
},
render : function()
{
$( '#container' ).append( $( this.el ).html(this.template( this.model.toJSON() )) );
return this;
},
savePlan : function( event )
{
this.model.set({
name : 'bad plan',
date : 'friday',
desc : 'blah',
id : Math.floor(Math.random()*11),
total_stops : '2'
});
this.collection.add( this.model );
app.navigate('', true );
event.preventDefault();
},
cancel : function(){}
});
Router (default method):
index : function()
{
this.container.empty();
var self = this;
//This is a hack to get this to work
//on default page load fetch all plans from the server
//if the page has loaded ( this.plans is defined) set the updated plans collection to the view
//There has to be a better way!!
if( ! this.plans )
{
this.plans = new Plans();
this.plans.fetch({
success: function()
{
self.plansListView = new PlansListView({ collection : self.plans });
$( '#container' ).append( self.plansListView.render().el );
if( self.requestedID ) self.planDetails( self.requestedID );
}
});
}
else
{
this.plansListView = new PlansListView({ collection : this.plans });
$( '#container' ).append( self.plansListView.render().el );
if( this.requestedID ) self.planDetails( this.requestedID );
}
},
New Plan Route:
newPlan : function()
{ var plan = new Plan({name: 'Cool Plan', date: 'Monday', desc: 'This is a great app'});
this.newPlan = new NewPlanView({ model : plan, collection: this.plans });
this.newPlan.render();
}
FULL CODE ( function( $ ){
var Plan = Backbone.Model.extend({
defaults: {
name : '',
date : '',
desc : ''
}
});
var Plans = Backbone.Collection.extend({
model : Plan,
url : '/data/'
});
$( document ).ready(function( e ){
var PlansListView = Backbone.View.extend({
tagName : 'ul',
initialize : function()
{
_.bindAll( this, 'render', 'close' );
//reset the view if the collection is reset
this.collection.bind( 'reset', this.render , this );
},
render : function()
{
_.each( this.collection.models, function( plan ){
$( this.el ).append( new PlansListItemView({ model: plan }).render().el );
}, this );
return this;
},
close : function()
{
$( this.el ).unbind();
$( this.el ).remove();
}
});//end
var PlansListItemView = Backbone.View.extend({
tagName : 'li',
template : _.template( $( '#list-item-template' ).html() ),
events :{
'click a' : 'listInfo'
},
render : function()
{
$( this.el ).html( this.template( this.model.toJSON() ) );
return this;
},
listInfo : function( event )
{
}
});//end
var PlanView = Backbone.View.extend({
tagName : 'section',
events : {
'click button.add-plan' : 'newPlan'
},
template: _.template( $( '#plan-template' ).html() ),
initialize: function()
{
_.bindAll( this, 'render', 'close', 'newPlan' );
},
render : function()
{
$( '#container' ).append( $( this.el ).html( this.template( this.model.toJSON() ) ) );
return this;
},
newPlan : function( event )
{
app.navigate( 'newplan', true );
},
close : function()
{
$( this.el ).unbind();
$( this.el ).remove();
}
});//end
var NewPlanView = Backbone.View.extend({
tagName : 'section',
template : _.template( $( '#plan-form-template' ).html() ),
events : {
'click button.save' : 'savePlan',
'click button.cancel' : 'cancel'
},
intialize: function()
{
_.bindAll( this, 'render', 'save', 'cancel' );
},
render : function()
{
$( '#container' ).append( $( this.el ).html(this.template( this.model.toJSON() )) );
return this;
},
savePlan : function( event )
{
this.model.set({
name : 'bad plan',
date : 'friday',
desc : 'blah',
id : Math.floor(Math.random()*11),
total_stops : '2'
});
this.collection.add( this.model );
app.navigate('', true );
event.preventDefault();
},
cancel : function(){}
});
var AppRouter = Backbone.Router.extend({
container : $( '#container' ),
routes : {
'' : 'index',
'viewplan/:id' : 'planDetails',
'newplan' : 'newPlan'
},
initialize: function(){
},
index : function()
{
this.container.empty();
var self = this;
//This is a hack to get this to work
//on default page load fetch all plans from the server
//if the page has loaded ( this.plans is defined) set the updated plans collection to the view
//There has to be a better way!!
if( ! this.plans )
{
this.plans = new Plans();
this.plans.fetch({
success: function()
{
self.plansListView = new PlansListView({ collection : self.plans });
$( '#container' ).append( self.plansListView.render().el );
if( self.requestedID ) self.planDetails( self.requestedID );
}
});
}
else
{
this.plansListView = new PlansListView({ collection : this.plans });
$( '#container' ).append( self.plansListView.render().el );
if( this.requestedID ) self.planDetails( this.requestedID );
}
},
planDetails : function( id )
{
if( this.plans )
{
this.plansListView.close();
this.plan = this.plans.get( id );
if( this.planView ) this.planView.close();
this.planView = new PlanView({ model : this.plan });
this.planView.render();
}
else{
this.requestedID = id;
this.index();
}
if( ! this.plans ) this.index();
},
newPlan : function()
{ var plan = new Plan({name: 'Cool Plan', date: 'Monday', desc: 'This is a great app'});
this.newPlan = new NewPlanView({ model : plan, collection: this.plans });
this.newPlan.render();
}
});
var app = new AppRouter();
Backbone.history.start();
});
})( jQuery );
Upvotes: 4
Views: 7874
Reputation: 4031
I think you are looking for the "remove" parameter, it will prevent models being removed from the collection during a fetch.
this.plans.fetch({remove: false})
See the backbone documentation for further details about the fetch method.
Upvotes: 0
Reputation: 7297
Are you fetching the data every time you hit the index page because you want the list to be in sync with the database (i.e. someone else added an item and you want it reflected)?
If that is not the case, what you are doing seems fine to me; you are only fetching when the data does not exist.
If you, however, do want the list to be in sync; but you still wish to have your newly added items rendered among them; you can either merge from the fetched list or use another list to hold the newly added data.
If you use another list, you just have to render both list; the unsaved list should probably exist either at the top or the bottom of the persisted list, grouped together.
Upvotes: 1