Backbone relation model and collection.length - backbone.js

Let's suppose I have the following model (model1) and collection (collection1)
model1.attributes = {
name: 'bar'
};
collection1.models = [{}, {}, {}];
It will be possible by using backbone relation to make the model1 to know about the length of collection1?
model1.attributes = {
name: 'bar',
collection1Length = 3 // collection1.models.length
}
Thanks

Based on your comments, it might be best to simply create a reference to the collection itself within the model:
ModelName = Backbone.Model.extend({
...
linked_collection: null // don't call this 'collection', as model.collection already exists
...
}
var model1 = new ModelName();
model1.set('linked_collection',collection1);
Now you can do this at any time to get the linked collection's length.
model1.get('linked_collection').length

Related

How to filter collection based on another collection using underscore.js

I have a collection called `mainItems``
this.mainItems;
which contains 18 models. And also one more collection object which contains selected items:
this.seletedItems;
I need to filter the main collection object based on other collection.
I have tried the below approach:
var that = this;
var model = _.reject(that.filteredItems.models, function(model1){
return _.filter(that.collection.models, function(model2) {
return model1.id == model2.id;
});
});
but this approach is not working properly. Is it possible to filter the main items by avoiding second iteration ?
Please help!
You can use the Underscore's methods proxied by Backbone to simplify your filter.
For example, to list the models in mainItems without the models in selectedItems, you could use
// reject the models found in selectedItems
var filtered = mainItems.reject(function(m) {
return selectedItems.get(m.id);
});
Note that Collection.get is a hash lookup, making this a Backbone equivalent to the answer pointed by #GruffBunny in the comments.
And a demo
var mainItems = new Backbone.Collection([
{id: 1},
{id: 2},
{id: 3},
{id: 4}
]);
var selectedItems = new Backbone.Collection([
{id: 2}
]);
var keep = mainItems.reject(function(m) {
return selectedItems.get(m.id);
});
console.log(_.pluck(keep, 'id'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
You can use difference to get all the models that are not selected.
var mainItems = [{ a: 1 }, { b: 2 }, { c: 3 }];
var selectedItems = mainItems.slice(1);
console.log(_.difference(mainItems, selectedItems));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

AngularJS and JSON returns undefined

Hi I have got a data in LocalStorage as JSON string:
[
{"Date":"28/04/2016","Time":"08:00","Title":"Title 1"},
{"Date":"28/04/2016","Time":"08:30","Title":"Title 2"}
]
And my module.factory looks like:
module.factory('$schedule', function() {
var schedule = {};
var result = JSON.parse(localStorage.getItem('myAgenda'));
schedule.items = [{
title: result.Title,
date: result.Date,
time: result.Time
}];
return schedule;
});
When I am trying to get data it returns undefined. When I try to get a specific object like:
console.log(result[0].Title);
It works fine and shows only the first element. I guess I missing each definition but don't know how to do it. Please help me to get all results in my schedule.items.
And I am passing the result as items into:
module.controller('ScheduleController', function($scope, $schedule) {
$scope.items = $schedule.items;
});
Many thanks.
You are trying to access fields in an array without mentioning wich array element you want to access. If you want to enumerate all agenda entries and add them to your array, it should look something like this:
module.factory('$schedule', function () {
var schedule = [];
var result = JSON.parse(localStorage.getItem('myAgenda'));
result.forEach(function (date) {
schedule.push({
title: date.Title,
date: date.Date,
time: date.Time
})
})
return schedule;
});
You should use .map over array, also add missing } in your last element of array.
var schedule = [];
//assuming result returns an array.
schedule = result.map(function(value){
return {
title: value.Title,
date: value.Date,
time: value.Time
};
})
Not familiar with module.factory, but it looks like result is an array of objects and you're accessing it like a single object when creating schedule.items.
You might have to iterate over the array and create an item per object in result.

Sorting a nested array from model in Ember?

So I have a model in Ember that is generating a hash with three objects. One of the objects is an array of objects with another array inside each object. I need to sort this innermost array, but I am having trouble doing so.
Here are my models.
App.Person = DS.Model.extend ({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
age: DS.attr('string'),
gender: DS.attr('string'),
innerMostArray: DS.hasMany('innerMostObject')
});
App.innerMostObject = DS.Model.extend ({
person_id: DS.belongsTo('person'),
attr1: DS.attr('string'),
attr2: DS.attr('string')
});
Here is my Route
App.NestedArrayRoute = Ember.Route.extend({
model: function(params) {
return Ember.RSVP.hash({
object1: this.store.find('object1', params.object1_id),
people: this.store.all('person'),
object3: this.store.all('object3')
});
},
afterModel: function(model, transition) {
model.people.forEach(function(item, index, enumerable){
var innerMostArray = item.get('innerMostArray');
var sortedArray = innerMostArray.sortBy('attr1', 'attr2');
});
model.people.update();
}
});
I know that I am nowhere near doing this right but I just don't know how to sort this nested array. I've seen examples of array controllers, but I don't know how to use one to sort this nested array. If anyone could give an example of how to do this it would be very helpful. Thank you.
I agree with Kalmans answer, but I suggest you do this sorting with built-in methods to save you trouble:
App.Person = DS.Model.extend({
name: DS.attr('string'),
fruits: DS.hasMany('fruit', {async: true}),
fruitSorting: ['title', 'color'],
sortedFruits: Ember.computed.sort('fruits', 'fruitSorting')
});
I forked his example here: http://emberjs.jsbin.com/manutu/1/edit?html,js,output
One way to do this is to create a computed property on the model as follows:
App.Person = DS.Model.extend({
name: DS.attr('string'),
fruits: DS.hasMany('fruit', { async: true }),
sortedFruits: function(){
var fruits = this.get('fruits');
return fruits.sortBy('title', 'color');
}.property('fruits.#each.title', 'fruits.#each.color')
});
Working example here

Merge local Backbone collection with server

I have a local Backbone collection:
var collection = new Backbone.Collection([ { greeting: "hi" }, { greeting: "bye" } ]);
I understand that when I run collection.fetch, Backbone will run collection.set on the results. I need to merge in the response from the server, however. Say the response is:
[ { id: "2", greeting: "hi", name: "Bob" } ]
I would like the resulting collection, after the merge, to be:
[ { id: "2", greeting: "hi", name: "Bob" }, { greeting: "bye" } ]
I understand Backbone already attempts to do some merging here, but if I set the example response above, no merge happens and a new model gets added instead. I assume this is because it merges by id, and here we do not have any ids (in the local collection). In this case, greeting is my unique identifier / key.
The reason I am trying to do this is because I have a local collection and I simply want to see what already exists from that collection (using the key greeting) and merge any findings in.
My solution:
feeds.fetch({
add: false,
remove: false,
merge: false,
data: params,
success: function (feeds, response) {
// Merge any matches
_.each(response.results, function (result) {
_.each(feeds.models, function (feed) {
// We have to `parse` the result before setting it, as Model#set does
// not automatically run `parse` (Collection#set does).
result = feed.parse(result)
if (feed.get('rssUrl') === result.rssUrl) feed.set(result)
})
})
cb(feeds)
}
})
You can tell backbone to use a different key for the id attribute on your model:
GreetingModel = Backbone.Model.extend({
idAttribute: "greeting"
});
GreetingCollection = Backbone.Collection.extend({
model: GreetingModel
});
http://backbonejs.org/#Model-idAttribute
Edit: I suppose you could use two separate collections for local and server side.
var localCollection = new Backbone.Collection([ { greeting: "hi" }, { greeting: "bye" } ]);
ServerCollection = Backbone.Collection.extend({
url: "/api/"
...
});
var serverCollection = new ServerCollection({});
serverCollection.on("reset", function() {
localCollection.each(function(localModel) {
var greeting = localModel.get("greeting");
serverModel = serverCollection.findWhere({greeting: greeting});
if(serverModel) {
localModel.set(serverModel.attributes);
}
});
});
serverCollection.fetch();

Getting models in nested collections in Backbonejs

I'm working in a collection that contains a model with collections of "itself". For example:
[{
id: 1
name: "John",
children: [
{
id: 32
name: "Peter",
children: []
},
{
id: 54
name: "Mary",
children: [
{
id:12,
name: "Kevin"
}
]
},
]
}]
Let say that I want to get the Kevin "user" by its Id. But all that I have is the "first collection". How can I do that?? And about setting a user within a collection? Another thing: Its possible to get all the Kevin "parents" from him? Like Mary and John?
Does anyone has come to a issue like that?
Thanks a LOT
Well I've made a recursive function on the User's Collection that seems to solved the problem for now ( the best of this is that I can use for retrieve a "deep" model and change it.). Something like that ( if someone has any suggestions, be free to give it a opinion ):
findUserById: function(id) {
var self = new Backbone.Collection(this.toJSON());
return thisCollection(id, this);
function thisCollection(id, collection, array) {
var innerSelf = collection || this;
var thisArray = array || [];
for(var i = innerSelf.models.length; i--;) {
if(innerSelf.models[i].get('id') == id) {
return [innerSelf.models[i]].concat([thisArray]);
}else {
if(innerSelf.models[i].get('children').length > 0) {
thisArray.push(innerSelf.models[i]);
return thisCollection(id, innerSelf.models[i].get('children'), thisArray);
}else {
innerSelf.remove(innerSelf.models[i]);
return thisCollection(id, self, []);
}
}
}
}
}
Basically I return an array with 2 items. The first is the record that I'm looking for and the second is an array with the parents of this user.
Underscore (which is a Backbone dependency, so you already have it) is great for this sort of thing; if you use its "map" function (which Backbone provides as a method on Collection) with its find function, you can do the following:
findPersonInPeopleCollection: function(nameWeAreLookingFor) {
function findChildren(person) {
if (!person.children) return [person];
var children = _.map(person.children, function(child) {
foundPeople.push(findChildren(child);
})
return _.flatten(children);
}
var allPeople = _.flatten(myCollectionOfPeople.map(findChildren));
return _(allPeople).find(function(person) {
return person.get('name') == nameWeAreLookingFor;
}
}
If you wanted to store the parents initially you could either add logic to your "Person" model class's initialize function, eg.
var Person = Backbone.Model.extend({
initialize: function() {
_.each(this.get('children'), function(child) {
child.parent = this;
}, this);
}
});
You could also do something similar by overriding your collection's add method, or adding an event handler to it that triggers after people get added.

Resources