I am using the backbone library to perform the following:
var Persons = Backbone.Collection.extend({
defaults: {
name: 'unknown',
age: 18
},
over_18: function () {
return this.filter(function (model) {
return model.get('age') > 18
});
},
under_18: function () {
var persons_over_18 = this.over_18;
return this.without(this, persons_over_18); // it does not work!! why?
}
});
persons = new Persons([{age: 17}, {age: 27}, {age:31} ]);
persons.under_18().length; // 3 instead of 1
As you can see the method under_18
is not working properly because it returns all the models
instead of giving me just the models which age attribute is under 18.
So in order to debug my code, I decided to see the the Backbone.js Annotated Source,in particular the following code:
var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', ... ]; // and more
_.each(methods, function(method) {
Collection.prototype[method] = function() {
var args = slice.call(arguments);
args.unshift(this.models);
return _[method].apply(_, args);
};
});
But the above piece of code is not clear to me and I still cannot make the first one work as I wish.
So my question is how can I fix the first code in relation to the second one?
Here is my code into jsfiddle.net http://jsfiddle.net/tVmTM/176/
Lenghty answer to better understand the Backbone code:
In javascript a "member" for an object can be referenced in two ways:
.
notation: foo.say('hello');
[...]
notation, with the string as an argument... a bit like an associative array:
foo["say"]('hello')
What happens in backbone is that each string in the methods
array, defined just above this method, is iterated and added to the Collection
prototype, so added to all classes that inherit (or extend) Collection
.
In the function, the arguments
keyword simply references all the arguments passed into the function, even if the signature is empty:
Collection.prototype[method] = function() { // <- empty signature here!
The slice with no arguments will transform the passed arguments into an array. Notice the use of slice.call(...)
here (and refer to this SO question).
var args = slice.call(arguments);
unshift
then adds the Collection models array to the beginning of the array.
args.unshift(this.models);
Then we are actually calling the Underscore method (using the [...]
notation) on the new array of arguments. Using apply, we pass the _
as the first argument which will become the this
scope (more info here)
return _[method].apply(_, args);
This allows you to do stuff like:
MyCollection.each(function (model) { ... });
instead of
_.each(MyCollection.models, function (model) { ... });
The resulting effect is identical! The former will just call the latter. :)
To answer your question, the problem in your case is that the _.without
method does not accept two arrays but an array followed by a list of arguments; the method you are looking for is called difference
(look at this SO question), but it is not mapped into the Collection, so you either map it yourself (replicating the code found in the Backbone source) or just use it directly:
return _.difference(this.models, this.over_18());
Working fiddle: http://jsfiddle.net/tVmTM/177/
In my opinion, better just keep using filter
as you did for the over_18
method... Even better, put the over_18
and under_18
as methods in the Model (where they belong) and from the collection just use those.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With