Update model within collection where model attribute is x - backbone.js

Within backbone I have a collection. Where I want to update a model or models within it if a model has a particular attribute value.
I know I can do something to the extent of
var model = myCollection.where({some_attr:'something'});
where model becomes the child object from the collection. However beyond that I seem to get lost. As none of the backbone functions seem to work on it. Example, I can't:
model.set({other_attr: 'changed value'});
model.save();
I just get an Uncaught TypeError. model.set is not a function
again.. Example sake.
So overall I want to find the model, update it, and then save it and its changes to the server? Idea's? Assume I have no workable ID to make getting and setting a little easier, hence why I need to do a where and update like that.

I would recommend using where and always working with the array of models it passes. When you're done updating the models you can make a sync call on the collection to bulk update those changes back to the server.
myCollection.
where({some_attr:'something'}).
map(function(model) {
model.set({other_attr: 'changed value'});
});
myCollection.sync('update');

The statement
var model = myCollection.where({some_attr:'something'});
returns an array of models, not a single model. Assuming only one model will match your criteria, you could use
var model = myCollection.findWhere({some_attr:'something'});
If your collection could have multiple matching models, then you'll want to stick with .where but iterate over the array.

Related

Updating a model attribute

I am practising Backbone js. I have a collection consisting of 10 models. In just one model, I want to change 2 of the attributes. I am using the device local storage. I have tried the following code (this is just part of the larger script):
this.collection.forEach(function(user) {
if (user.get('subject') === 'Physics') {
user.set({'title': 'hdrodynamics'});
user.save();
console.log(JSON.stringify(user));
return;
}
});
I consider this method inefficient. What happens as the collection length increases. I believe there is a better way than this.
You can use collection.findWhere, which:
directly returns only the first model in the collection that matches the passed attributes.
Which can reduce the number of iterations run.
You can also use model.save with attributes to avoid doing the extra 'set'.
// Stops when matching model is found
var physicsModel = this.collection.findWhere({ subject: 'Physics' });
// might want to do some null-checking...
// Performs a set prior to POSTing, firing correct 'change' events
physicsModel.save({ title: 'hydrodynamics' });
Note: A more efficient way to do this would be to avoid the lookup in the collection. For example, if this model should 'save' by some action from the user, you could leverage the model's view to gain access to the correct model (i.e. this.model.save()). This of course is implementation dependent.

GAE filter by missing repeated property

I'm trying to query for all objects that have no value for a given repeated property.
For example imagine you have the following model:
class Foo(ndb.Model):
bar = ndb.IntegerProperty(repeated=True)
and you wanted all the instances of Foo where bar had no value, or is []. How would you perform this query or work around this behavior?
Note (from GAE's ndb documentation):
Querying for a value of None on a repeated property has undefined
behavior; don't do that
Well, like the docs say, you can't.
One way of approaching this might be to keep another property on the model that records how many values it has in bar. You would need to update this when the entity is saved: a good way would be to override put() to do self.bar_count = len(self.bars) before calling the superclass method.
Of course, you'd then need to go through your existing data to set the counts; you might want to use a mapper to do that.

Backbone keeps creating new models

Everytime I do a "fetch" of my collection, backbone creates new models for every item. The old models stick around in memory, causing a big memory leak.
There are no changes of the data between "fetch" calls, should backbone not recognize that there are no changes and carry on?
Backbone collection will use "set" method to update model data in collection.
Set is a smart method and it will perform these checks when collection fetch data from server:
Add: If a model in the list isn't yet in the collection it will be added.
merge: if the model is already in the collection its attributes will be merged
remove: if the collection contains any models that aren't present in the list, they'll be removed
Your problem is the collection will continuously add new model when you fetch data from server even though you just try to refresh data which is updated. I think your data don't have unique "id" attribute. So the collection can't perform "smart update" when it fetch data from server.
{id:"1234",name:"blabla","tel:0600000000"}
I hope this is helpful for you.
The problem is different than I thought.
I keep a list of sub-view in my view so I can remove them. However, there appear to be 2 different properties with the same name. One keeping a hold of the views.

Should model know its collection?

A collection knows its models, but is there a way for the model to get to its collection?
I would need a way to get to the "adjacent" models in a collection. The following may give a better idea of the problem:
I'm trying to implement a simple app that ranks items based on an "priority" attribute in the model and maintains a list of them. Each model view has buttons for increasing or decreasing the priority, which should alter the ordering of the list accordingly.
Simply adding or subtracting one from the current value will work as long as the priority-attributes are continuous, for example:
1,2,3,4,5,6
But there is also a delete button, which allows deleting any model from the list. It can lead to priority-attributes like:
1,2,5,6
Now changing the priority of 5 to 4 wont change the order of the list as it's still greater than 2.
It's undocumented, but the models will automatically have a collection property that points to the collection. I don't know the details of how that works if a model is in multiple collections (perhaps the collection property is not changed once it's set).
To solve the specific problem you mentioned, you can listen for remove events on the collection and adjust things accordingly. In this case you don't even need that collection property, because both the model and collection will be passed to the remove listener.
var handler = function ( model, collection, options ) {
// ...
};
collection.on( 'remove', handler );
The Backbone.Collection docs also say this:
the index at which the model is being removed from the collection is available as options.index
The options argument to remove listeners is not documented where the signature for remove listeners is documented, but it is passed (in 0.9.2 anyway).

Approach to hide/show Backbone model views

Help me to find best approach for managing Backbone views.
For example I have a collection of views MyCollectionView which consists of MyModelView - views of each model in the collection.
And what if I want to hide/show some models on the page?
Now I am following this way:
Use collection.each for every model
Inside of loop I call model function filter with some params
In this function I check model properties and call model.trigger 'hide' or model.trigger 'show'
And finally in the model view I use this.model.bind 'hide', this.hide, this where actually I use .hide() or .show()
This way seems me awful... Why do I need to do this long chain of functions and events. Does any simplest approach exist?
Thanks!
Your models shouldn't be telling views what they should do - they are supposed to represent data and shouldn't take part in controlling the application - so no wonders it feels wrong to you :)
The more elegant way would be to add a filter method to the MyCollectionView which would use underscore methods to filter the views you want to show/hide and do its job of well... doing that - picking which models should be shown. Then having hte array of matches just call a method for rendering the list and pass your array of models to it so it can render the views for matching models.
From my experience of creating such filters I can tell you that it might be much more efficient for longer lists to remove whole list do filtering on collection level and render again only the views which are matching the filter query. jQuery hide/show can be a bit taxing - though it's a concern you will have only with large amount of data/views.
Also! Take advantage of the underscore methods bound to the collection - you don't need to do collection.each(... you can just do
var matches = collection.filter(function(model) {
return /*matching condition*/;
});
(Also remember to use documentFragment when rendering lists and appending to the DOM pre-generated list of views rather then appending them one by one)

Resources