remove model in collection and fire remove event - backbone.js - javascript

how do I remove remove a model in collection and make the remove event fire . I tried people.remove([{ name: "joe3" }]); but it wont work.
var Person = Backbone.Model.extend({
initialize: function () {
console.log(" person is initialized");
},
defaults: {
name: "underfined",
age:"underfined"
}
});
var People = Backbone.Collection.extend({
initialize: function () {
console.log("people collection is initialized");
this.bind('add', this.onModelAdded, this);
this.bind('remove', this.onModelRemoved, this);
},
model: Person,
onModelAdded: function(model, collection, options) {
console.log("options = ", options);
alert("added");
},
onModelRemoved: function (model, collection, options) {
console.log("options = ", options);
alert("removed");
},
});
//var person = new Person({ name: "joe1" });
var people = new People();
//people.add([{ name: "joe2" }]);
people.add([{ name: "joe1" }]);
people.add([{ name: "joe2" }]);
people.add([{ name: "joe3" }]);
people.add([{ name: "joe4" }]);
people.add([{ name: "joe5" }]);
people.remove([{ name: "joe3" }]);
console.log(people.toJSON());

For anyone else looking for a remove where, you can simply chain it with a collection.where call. like so to remove all items matching the search:
people.remove(people.where({name: "joe3"}));
see Backbone collection.where

By doing:
people.remove([{ name: "joe3" }]);
you don't remove a model, because you pass just an plain object which is not connected to people collection. Instead you could do something like this:
people.remove(people.at(2));
Or:
var model = new Person({name: "joe3"});
people.add(model);
...
people.remove(model);
will work as well.
So you need to reference actual model object from a collection;
http://jsfiddle.net/kD9Xu/

Another way is shorter a little and fires the remove event for a collection as well:
people.at(2).destroy();
// OR
people.where({name: "joe2"})[0].destroy();
Triggers a "destroy" event on the model, which will bubble up through any collections that contain it.. http://backbonejs.org/#Model-destroy

var Person = Backbone.Model.extend({
defaults: {
name: "underfined",
age:"underfined"
}
});
var People = Backbone.Collection.extend({
initialize: function () {
this.bind('remove', this.onModelRemoved, this);
},
model: Person,
onModelRemoved: function (model, collection, options) {
alert("removed");
},
getByName: function(name){
return this.filter(function(val) {
return val.get("name") === name;
})
}
});
var people = new People();
people.add(new Person({name:"joe1"}));
people.add(new Person({name:"joe2"}));
people.remove(people.getByName("joe1"));
console.info(people.toJSON());

In order to remove "[0]" you can use the following code:
people.findWhere({name: "joe2"}).destroy();

Related

how to access changed attributes after fetch? backbone.js

I'm using this code to fetch a model from a server:
var id = args.model.get('id');
var options = {
action: 'getSubscriber',
address: args.model.get('address'),
silent: true
};
new Subscriber({ id: id }, options).fetch({
success: function(model, response) {
console.log(response);
console.log(model);
}
});
the response object contains all the data I need whereas model stores the data not as its direct attributes but as changed object. Is it wrong?
Usually I access model attributes with help of model.get('name') call. How do I access fresh attributes in that case? Should it be model.changed.thePropertyIwantToAccess?
You can use this change event
this.model.on('change', function () {
var changedAttributes = this.model.changedAttributes();
//Process the changed attributes
}, this);
Bind this events in the initialize function of the View
Ended up with this:
var Subscriber = Backbone.Model.extend({
defaults: {
id: null,
name: null,
status: null
// ...
},
initialize: function(attributes, options) {
var _this = this;
this.options = options || {};
this.on('change', function() {
_this.set(_this.changedAttributes()['0']);
});
}
// ...

backbone model get throws undefined error

app.Model.Brand = Backbone.Model.extend({});
app.Collection.BrandCollection = Backbone.Collection.extend({
model: app.Model.Brand,
parse: function (response) {
return response.brandDTOList;
}
});
var brandcollection = new app.Collection.BrandCollection();
brandcollection.url = '/brands';
brandcollection.fetch({
success: function (collection, response, options) {
app.views.brandline = new app.View.BrandPanelView({
model: brandcollection
});
$('#tab-content').empty();
$('#tab-content').append(app.views.brandline.render());
}
});
In this view a single model is passed.
app.View.BrandItemPanelView = Backbone.View.extend({
initialize: function (options) {
this.views = {};
this.model.bind('destroy', this.remove, this);
},
events: {
'click .bra-edt': 'brandEditAction',
},
brandEditAction: function () {
this.model.get('image');
}
});
When i do this.model.get('image'); i get get is not a function.
I am not sure why i am getting such a error.
I tried to deep clone it but still no success. The values are there.
var newModel = new app.Model.Brand();
newModel = $.extend(true, {}, self.model);
newModel.get('image')
Your problem is where you are passing in your BrandCollection, instead of passing it in as a collection you are passing it in as a model.
So instead of
app.views.brandline = new app.View.BrandPanelView({
model: brandcollection
});
You probably want to do
app.views.brandline = new app.View.BrandPanelView({
collection: brandcollection
});
How are you instantiating the BrandItemPanelView?
You will have to pass in the model explicitly:
var view = new app.View.BrandItemPanelView({ model: myModel });
If myModel is in fact a Backbone model, it ought to have the get function...
view.model // should return your model
view.brandEditAction() // should run the function and get the 'image' attribute from your model

Backbone error can not call method 'toJSON'?

I wanna render every waiter from my collection but console still show me error :
Uncaught TypeError: Cannot call method 'toJSON' of undefined
this is my code :
(function() {
window.App = {
Models: {},
Views: {},
Collections: {}
};
window.template = function(id) {
return _.template( $('id' + id).html() );
},
// WAITER MODEL
App.Models.Waiter = Backbone.Model.extend({
defaults: function() {
return {
title: 'Waiter Name',
id: []
};
}
});
// A LIST OF WAITERS COLLECTION
App.Collections.Waiters = Backbone.Collection.extend({
model: App.Models.Waiter
});
// VIEW FOR ALL WAITERS
App.Views.Waiters = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.collection.each(function(waiter) {
var waiterView = new App.Views.Waiter({ model: waiter });
this.$el.append(waiterView.render().el);
}, this);
return this;
}
});
// A VIEW FOR ONE PERSON
App.Views.Waiter = Backbone.View.extend({
tagName: 'li',
template: _.template("<%= title %><%= id %>"),
render: function() {
this.$el.html( this.template(this.model.toJSON()) );
return this;
},
});
waitersCollection = new App.Collections.Waiters([
{
title: 'ferko fristansky',
id: 2
},
{
title: 'ferko bandaska',
id: 3
},
{
title: 'fvwerv fristansky',
id: 4
}
]);
var waitersView = new App.Views.Waiter({ collection: waitersCollection });
$(document.body).append(waitersView.render().el);
})();
You're creating your waiterView with a collection:
var waiterView = new App.Views.Waiter({ collection: waitersCollection });
but App.Views.Waiter is a model-based view; that means that this.model will be undefined inside your App.Views.Waiter and so this will fail:
this.$el.html( this.template(this.model.toJSON()) );
// this is undefined -------------^^^^^
You probably want to create an App.Views.Waiters instead:
var waitersView = new App.Views.Waiters({ collection: waitersCollection });
Then, inside App.Views.Waiters, you'd create one App.Views.Waiter for each model in the collection rather than a new App.Views.extend({ model: waiter }):
render: function() {
this.collection.each(function(waiter) {
var waiterView = new App.Views.Waiter({ model: waiter });
this.$el.append(waiterView.render().el);
}, this);
return this;
}
As an aside, be careful with this:
App.Models.Waiter = Backbone.Model.extend({
defaults: {
title: 'Waiter Name',
id: []
}
});
The values from defaults are shallow copied so everything that uses those defaults will end up using exactly the same id array and that can lead to strange bugs when you have several models sharing the same id array. If you have mutable values in defaults, you usually want to use a function instead so that everyone gets their own distinct values:
App.Models.Waiter = Backbone.Model.extend({
defaults: function() {
return {
title: 'Waiter Name',
id: []
};
}
});

How to use the original Backbone collection after filtering?

I'm relatively new to Backbone and though I know the general idea of how to use it, my learning has been rapid and I'm probably missing some key elements.
So I have a collection that contains an attribute called "type" which can be article, book, video, class. I have the view rendering and everything but I need to be able to filter the collection when links are clicked.
My question is - how can I get it to filter down the collection and still be able to refilter the original collection when I click on another type?
Here's the gist of my code, I simplified it for easy reading:
var TagsView = Backbone.View.extend({
initialize: function(query) {
this.collection = new TagsCollection([], {query: self.apiQuery} );
this.collection.on('sync', function() {
self.render();
});
this.collection.on('reset', this.render, this);
},
render: function() {
//renders the template just fine
},
filter: function() {
//filtered does work correctly the first time I click on it but not the second.
var filtered = this.collection.where({'type':filter});
this.collection.reset(filtered);
}
});
update: I managed to get this working. I ended up triggering a filter event.
var TagsCollection = Backbone.Collection.extend({
initialize: function(model, options) {
this.query = options.query;
this.fetch();
},
url: function() {
return '/api/assets?tag=' + this.query;
},
filterBy: function(filter) {
filtered = this.filter(function(asset) {
return asset.get('type') == filter;
});
this.trigger('filter');
return new TagsCollection(filtered, {query: this.query});
},
model: AssetModel
});
And then in my view, I added some stuff to render my new collection.
var TagsView = Backbone.View.extend({
initialize: function(query) {
this.collection = new TagsCollection([], {query: self.apiQuery} );
this.collection.on('sync', function() {
self.render();
});
this.collection.on('filter sync', this.filterTemplate, this);
this.collection.on('reset', this.render, this);
},
render: function() {
//renders the template just fine
},
filterCollection: function(target) {
var filter = $(target).text().toLowerCase().slice(0,-1);
if (filter != 'al') {
var filtered = this.collection.filterBy(filter);
} else {
this.render();
}
},
filterTemplate: function() {
filterResults = new TagsCollection(filtered, {query: self.apiQuery});
console.log(filterResults);
$('.asset').remove();
filterResults.each(function(asset,index) {
dust.render('dust/academy-card', asset.toJSON(), function(error,output) {
self.$el.append(output);
});
});
},
});
The reason it's not working a second time is because you're deleting the models that don't match your filter when you call reset. That's normal behaviour for the reset function.
Instead of rendering with the view's main collection, try using a second collection just for rendering which represents the filtered data of the original base collection. So your view MIGHT look something like:
var TagsView = Backbone.View.extend({
filter: null,
events: {
'click .filter-button': 'filter'
},
initialize: function (query) {
this.baseCollection = new TagsCollection([], {query: self.apiQuery} );
this.baseCollection.on('reset sync', this.filterCollection, this);
this.collection = new Backbone.Collection;
this.collection.on('reset', this.render, this);
},
render: function () {
var self = this,
data = this.collection.toJSON();
// This renders all models in the one template
dust.render('some-template', data, function (error, output) {
self.$el.append(output);
});
},
filter: function (e) {
// Grab filter from data attribute or however else you prefer
this.filter = $(e.currentTarget).attr('data-filter');
this.filterCollection();
},
filterCollection: function () {
var filtered;
if (this.filter) {
filtered = this.baseCollection.where({'type': this.filter});
} else {
filtered = this.baseCollection.models;
}
this.collection.reset(filtered);
}
});
To remove any filters, set a button with class filter-button to have an empty data-filter attribute. collection will then be reset with all of baseCollection's models
Here's a better answer to this. Instead of making it so complicated, you can just use the where method. Here's my replacement solution for the question above.
filterby: function(type) {
return type === 'all' ? this : new BaseCollection(this.where({type: type});
});
You can try using comparator function of your Collection.
http://backbonejs.org/#Collection-comparator
Basically its is like sorting your collection.

Update a Backbone model property of type array by pushing

Is this really the best way to add an item to an array in a Backbone Model?
// TODO: is there a better syntax for this?
this.set(
'tags',
this.get('tags').push('newTag')
)
You can implement model.push like this:
var model, Model;
Model = Backbone.Model.extend({
defaults: { tags: [] },
push: function(arg, val) {
var arr = _.clone(this.get(arg));
arr.push(val);
this.set(arg, arr);
}
});
model = new Model;
model.on("change:tags", function(model, newTags) {
console.log(newTags)
});
model.push("tags", "New tag1")
model.push("tags", "New tag2")
But maybe you should store tags in Collection, listen to its events and update models tags attribute.
var model, Model, Tags, Tag;
// Override id attribute for Tag model
Tag = Backbone.Model.extend({
idAttribute: "name"
});
Tags = Backbone.Collection.extend({model: Tag});
Model = Backbone.Model.extend({
initialize: function() {
this.tags = new Tags;
this.tags.on("add remove reset", this.updateTags, this);
},
updateTags: function() {
this.set("tags", this.tags.pluck("name"))
}
});
model = new Model;
model.on("change:tags", function(model, newTags) {
console.log(newTags)
});
// Reset tags
model.tags.reset([{name: "New tag1"}, {name: "New tag2"}]);
// Add tags
model.tags.add({name: "New tag3"});
// Remove tag
model.tags.remove(model.tags.get("New tag3"));
If you have a model with an attribute that is an array like this
TestModel = Backbone.Model.extend({
defaults:{
return {
things:[]
}
}
});
to add an item to things on model TestModel
var test = new TestModel;
test.set({things:item});

Categories