Adding model to a collection after save method in backbone - backbone.js

I am using the save method when my data is submitted. On success callback of the save method, the collection should be updated with the model which i have saved since i want to get the id of the model from my server. My code is as below
var app = app || {};
app.AllDoneView = Backbone.View.extend({
el: '#frmAddDone',
events:{
'click #addDone':'addDone'
},
addDone: function(e ) {
e.preventDefault();
var formData = {
doneHeading: this.$("#doneHeading").val(),
doneDescription: this.$("#doneDescription").val(),
};
var donemodel = new app.Done();
donemodel.save(formData,
{
success :function(data){
/*my problem is here how do i listen to collection event add that has been
instantiated in intialize property to call renderDone . My tried code is
var donecollection = new app.AllDone();
donecollection.add(donemodel);
and my response from server is
[{id:145, doneHeading:heading , doneDescription:description,
submission_date:2014-08-27 03:20:12}]
*/
},
error: function(data){
console.log('error');
},
});
},
initialize: function() {
this.collection = new app.AllDone();
this.collection.fetch({
error: function () {
console.log("error!!");
},
success: function (collection) {
console.log("no error");
}
});
this.listenTo( this.collection, 'add', this.renderDone );
},
renderDone: function( item ) {
var doneView = new app.DoneView({
model: item
});
this.$el.append( doneView.render().el );
}
});
Collection is
var app = app || {};
app.AllDone = Backbone.Collection.extend({
url: './api',
model: app.Done,
});
Model is
var app = app || {};
app.Done = Backbone.Model.extend({
url: "./insert_done",
});
View is
var app = app || {};
app.DoneView = Backbone.View.extend({
template: _.template( $( '#doneTemplate' ).html() ),
render: function() {
function
this.$el.html( this.template( this.model.attributes ) );
return this;
}
});

In your success callback you create an entirely new collection, which doesn't have any listeners registered. This is the reason why the renderDone isn't triggered.
The model you receive from the server should be added to the collection which is attached directly to your view, this.collection:
var self = this,
donemodel = new app.Done();
donemodel.save(formData, {
success :function(data){
// this is the collection you created in initialize
self.collection.add(donemodel);
},
error: function(data){
console.log('error');
}
});

Related

How to filter a backbone collection and render the results

Total newbie to Backbone so apologize if this is a simple question.
I am have successfully loaded a collection and rendered the view. However I have a dropdown with A tags that I would like to use the filter the data displayed. I'm trying to set an event listener in my VIEW and then trigger a function within the view to filter the results and re-render the view.
Here's my code:
IBA.NewsModel = Backbone.Model.extend({});
IBA.NewsCollection = Backbone.Collection.extend({
model: IBA.NewsModel,
url: "/news/api"
});
IBA.NewsView = Backbone.View.extend({
el: '#main',
template: _.template($("#news-article").html()),
events: {
"change .dropdown-item": "filterNews"
},
initialize: function () {
this.collection = new IBA.NewsCollection();
this.listenTo(this.collection, 'reset', this.render);
this.collection.fetch({
success: function() {
console.log("JSON file load was successful");
view.render();
},
error: function(){
console.log('There was some error in loading and processing the JSON file');
}
});
},
render: function () {
this.$el.html(this.template({
articles: this.collection.toJSON()
})
);
return this;
},
filterNews: function (e){
e.preventDefault();
var items = this.collection.where({cat_name: "interviews"});
console.log(items);
this.$el.html(this.template({articles: this.items.toJSON()}));
}
});
var view = new IBA.NewsView();
Easiest way to do it would be to reset the actual collection with the filtered results:
IBA.NewsModel = Backbone.Model.extend({});
IBA.NewsCollection = Backbone.Collection.extend({
model: IBA.NewsModel,
url: "/news/api"
});
IBA.NewsView = Backbone.View.extend({
el: '#main',
template: _.template($("#news-article").html()),
events: {
"change .dropdown-item": "filterNews"
},
initialize: function() {
this.collection = new IBA.NewsCollection();
this.listenTo(this.collection, 'reset', this.render);
this.fetchNews();
},
fetchNews: function() {
var view = this;
this.collection.fetch({
success: function() {
console.log("JSON file load was successful");
view.render();
},
error: function() {
console.log('There was some error in loading and processing the JSON file');
}
});
},
render: function() {
this.$el.html(this.template({
articles: this.collection.toJSON()
}));
return this;
},
filterNews: function(e) {
e.preventDefault();
this.collection.reset(this.collection.where({
cat_name: "interviews"
}));
}
});
var view = new IBA.NewsView();
When you want to go back to original data, fetch it again using the fetchNews() method.
You also had a syntax error that view was not defined in your initialize

How to delete a dependent model from the same collection in backbone.js

I have a model which has both navid and subnavid .While destroying a model i need to check in the entire collection , for other models which have navid as same as subnavid of the model i'am trying to delete . Please help me out . Thanks in advance . Heregoes my sample code.
Model:
var Node = Backbone.Model.extend({
defaults: {
NavId: '',
SubNavId: ''.
ItemName:''
} }
Collection:
var NodeCollection = Backbone.Collection.extend({ model:Node }
And i have two view one for the Node(i am building tr) and other for
the collection(I need to build table) var NodeCollectionView =
Backbone.View.extend({
initialize: function (options) {
var self = this; self.collection = new NodeCollection({ NavigationId: options.NavigationId });
self.collection.fetch({
success: function () {
/*I am getting hte proper collection from my restful api and iam able to bind it properly
self.render();
}
});
},
render: function () {
var that = this;
_.each(this.collection.models, function (item) {
that.RenderEachNode(item);
}, this);
},
RenderEachNode: function (item) {
var TempJsonNode = item.toJSON();
var self = this;
var nodeView = new NodeView({
tagName: 'tr',
id: 'NavId_' + TempJsonNode.NavItemId,
model: item
});
} });
var ItemTemplate = ""; ItemTemplate += " <td>"; ItemTemplate += " <a><%= ItemName %></a>"; ItemTemplate +=" </td>"; ItemTemplate
+=" <td>"; ItemTemplate +=" <a href='#' original-title='Delete ' class='tip_north Delete'>X</a>"; ItemTemplate +=" </td> ";
var NavigationItemView = Backbone.View.extend({
template: ItemTemplate,
render: function () {
var self = this;
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
},
events: {
"click .Delete": "DeleteBtnClick"
},
DeleteBtnClick: function () {
var self = this;
self.model.destroy({
success: function (status, data) {
var RetData = JSON.parse(data);
if (RetData.Status == 'Success') {
$(self.el).remove()
}
},
error: function () {
alert('Error In Deleting The Record');
}
});
return false;
} });
I am able to build the table properly but while destroying a model , i am not figuring out a way to destroy the dependent models.My Api is restricted in such a way that i cannot get a nested json ( if so i would have done with backbone relation). so i need to figure out some way that the other models and views which has the NavId of the model am deleting.
Please help me out.
How about something like:
var NodeView = Backbone.View.extend({
initialize: function() {
//when the model gets destroyed, remove the view
this.listenTo(this.model, 'destroy', this.remove);
},
//..clip
DeleteBtnClick: function () {
var self = this;
var collection = self.model.collection;
var navId = self.model.get('NavId');
self.model.destroy({
success: function (status, data) {
var RetData = JSON.parse(data);
if (RetData.Status == 'Success') {
//if parent was part of a collection
if (collection) {
//find related models
var related = collection.filter(function (model) {
return model.get('SubNavId') === navId;
});
//call destroy for each related model.
var promises = _.invoke(related, 'destroy');
//optional: if you want to do something when all the children
//are destroyed:
$.when.apply($, promises).then(function () {
console.log('all destroyed');
});
}
}
},
error: function () {
console.log(arguments);
alert('Error In Deleting The Record');
}
});
return false;
}
});
Edit: JSFiddle here

Problems with passing model to view from local storage

During the initialize function of my app I would like to default to my search page and pass my LeagueCollection as the model.
I am encountering an issue where I can add a watch to this.searchResults in my App initialize and see models: Array[3] as expected,
but when the this.model.toJSON() in the view is called I get the error object has no method toJSON.
This code was working fine with a in memory collection and then I switched to using backbone.localstorage.js to store the app data locally.
So my question is: why is the model not populated in the view?
In my main.js I have
var AppRouter = Backbone.Router.extend({
routes: {
"": "list",
...
},
initialize: function () {
this.searchResults = new LeagueCollection();
this.searchPage = new SearchPage({
model: this.searchResults.fetch()
});
this.searchPage.render();
},
...
});
In my Search Page view
window.SearchPage = Backbone.View.extend({
initialize:function () {
this.template = _.template(tpl.get('search-page'));
},
render:function (eventName) {
var self = this;
$(this.el).html(this.template(this.model.toJSON()));
this.listView = new LeagueListView({el: $('ul', this.el), model: this.model});
this.listView.render();
return this;
},
...
});
The method collection.fetch doesn't return the collection -- it's asynchronous. What you probably want is to use its success callback:
this.searchResults = new LeagueCollection();
var self = this;
this.searchResults.fetch({
success: function(collection, response) {
self.searchPage = new SearchPage( { model: collection } );
self.searchPage.render();
}
});

Backbone.js and Bootstrap Typeahead - rendering after async fetch

I took the base of this code from a gist. It initially worked perfectly when I first fetch()ed the collection and then in render() called tw-bootstap's .typeahead().
However, I have put in a keypress event to try and restrict the size of the data returned by fetch(). The collection data is returned and it is filtered through prepData() fine and arrives at render(). The typeahead is not working, however at that stage. It may be that the backbone event is overriding render at that point?
// typeahead on the numbers
var Bootstrap = {};
Bootstrap.Typeahead = Backbone.View.extend({
el: '#autocompleteN',
tagName: 'input',
attributes: {"data-provide": "typeahead"},
initialize: function(options){
if(!this.collection) {
return null;
}
//this.collection.on("reset", this.prepData, this);
},
events: {
"keypress": "setSearch"
},
setSearch: _.throttle(function(e) {
var that=this;
var d = e.currentTarget.value;
// strip spaces and remove non-numerics
d = d.replace(/ /g,'');
d = d.replace(/[^0-9]/g, '');
// if it's longer than 2, call a fetch;
if(d.length > 2) {
$.when( app.searchNums.fetch({url: 'api/index.php/search/num/'+d}) ).then(function() {
//console.dir("success");
that.prepData();
});
}
}, 1000),
prepData: function() {
//console.dir("prepData called");
var prepare = _.pluck(this.collection.models, 'attributes');
this.property = this.options.property || _.keys(prepare[0])[0];
this.items = this.options.items;
this.data = _.pluck(prepare, this.property);
this.render();
},
render: function() {
var that = this;
that.$el.typeahead({
source: that.data,
//source: ['PHP', 'MySQL', 'SQL', 'PostgreSQL', 'HTML', 'CSS', 'HTML5', 'CSS3', 'JSON'],
items: that.items,
onselect: function( data ) {
// render the results view here
}
});
return this;
}
});
var bui = new Bootstrap.Typeahead({
collection: app.searchNums,
items: 5
});
Why dont you just set minLength on the typeahead, it looks like that is what you are trying to do?

_.delay call from inside a Backbone.View event handler

I have a relatively simple Backbone handled message container. I can add messages, and they display fine, but adding a delay to close them automatically does not work as undefined is passed on to the callback.
Could someone tell me how to properly specify the argument to be called? The main line is _.delay(function(){ messages.remove(this.model) }, 3000);
var Message = Backbone.Model.extend({
defaults: {
message: 'No message',
type: 'error'
},
/*validate: function( attrs ) {
if ( !attrs.type in ['error', 'warning', 'success', 'info'] ) {
return 'Wrong message type given';
}
}*/
});
var Messages = Backbone.Collection.extend({
model: Message
})
var messages = new Messages;
var MessageView = Backbone.View.extend({
template: Handlebars.compile( $('#t-message').html() ),
initialize: function() {
messages.bind('delete', this.remove, this);
},
render: function( event ) {
$(this.el).html( this.template( this.model.toJSON() ) );
// TODO: this never fires properly
_.delay(function(){ messages.remove(this.model) }, 3000);
return this;
}
});
var MessageContainerView = Backbone.View.extend({
id: 'messages',
initialize: function() {
messages.bind('add', this.addMessage, this);
},
render: function( event ) {
return this;
},
addMessage: function( message ) {
var view = new MessageView({model: message});
$('#' + this.id).append(view.render().el);
}
});
var messagecontainerview = new MessageContainerView;
messages.add(new Message({message: 'Close this in 3 secs.', type: 'success'}))
Of course this.model is defined inside the render method, but it's undefined in the remove call.
It's because this is bound to whatever is calling the delay callback function, not the view. Before you call _.delay assign var self=this; Then use self.model in the callback function.

Resources