Backbone.js How to shuffle items within a Collection - backbone.js

I'm having issues when trying to randomize the order of objects in a collection.
Here's the code that I've tried:
console.log(this.collection);
shuffled = this.collection.shuffle();
console.log(shuffled);
And here's the console output (using a test collection with 3 items):
child {models: Array[3], length: 3, _byId: Object, url: "/myurl/myid", _listenerId: "l7"…}
_byId: Object
_events: Object
_idAttr: "id"
_listenerId: "l7"
length: 3
models: Array[3]
__proto__: Surrogate
[child, child, child]
0: child
1: child
2: child
length: 3
__proto__: Array[0]
As you can see, the Collection is not being shuffled properly, instead it's creating a new unuseable object full of pesky children.
All I'm trying to do is randomize the order in which the models appear in the collection before passing it to a view for display (I'm creating a button called "randomize" which needs to randomize the display of the items in the collection). I thought this would be an easy task, but at this point I'm considering just creating a whole new model and doing the shuffle on the server.
Any help is greatly appreciated!

console.log(this.collection);
this.collection.reset(this.collection.shuffle(), {silent:true});
console.log(this.collection);

Related

angular multidimensional array live update

I have an endpoint that has 5 users in a obj with 5 arrays. every 5 data changes for a few properties. I want to get the dynamic property, and push it to an array for each user. How can i do this? I need each user to have their own dynamic array.
watchMe = [{user: 1 hr: 10}, {user: 2 hr: 20}, {user: 3 hr: 30}];
updateMe=[];
function getLive(){
angular.forEach(watchMe, function(value, key){
updateMe.push(value.hr);
console.log (updateMe)
})
};
$interval(getLive, 5000);
I think using a utility library like lodash could provide you with what you're after.
Namely: whenever the interval hits you can use _.filter to get you the objects containing the dynamically added property - or just _.map() through them and pull the property (if it exists).
Disclaimer: I would have preferred to add this feedback as a comment but I do not have the rep yet

Handlebars nested objects

I am using backbone.js with nested models. The idea being self-contain all attributes of the author as well as to re-use the author model on both posts and comments. At least in theory this makes sense I would think.
However, setting things up like this I've run into a confusion how to retrieve the different values with Handlebars. Handlebars doesn't like being passed objects from what I've read. I can easily retrieve the status in a {{#each}} with {{status}} but naturally doing {{author.name}} won't work.
I've looked at using a Helper, however as you can see I have comments nested inside, that will have another author nested inside. I don't believe helpers inside helpers will work.
This is a sample object pulled from the console of Chrome.
Object {items: Array[2]}
+items: Array[2]
+0: Object
+author: child
_changing: false
_pending: false
_previousAttributes: Object
+attributes: Object
name: "Amy Pond"
profileImage: "Amy.jpg"
__proto__: Object
changed: Object
cid: "c0"
__proto__: Surrogate
comments: child
id: "50f5f5d4014e045f000001"
status: "1- This is a sample message."
__proto__: Object
+1: Object
author: child
comments: child
id: "50f5f5d4014e045f000002"
status: "2- This is another sample message."
__proto__: Object
length: 2
__proto__: Array[0]
__proto__: Object
Am I incorrect in my organization, or is there a better way to handle multidimensional data? Or is there a nice way for Handlebars to get to each of the values?
If there is a more powerful templating engine, I'm open options.
Seems the problem is that you put the Backbone model directly into your template, but you have to convert it into a JSON object first using model.toJSON(). Or you try to access author.attributes.name.
From the docs:
Handlebars also supports nested paths, making it possible to look up
properties nested below the current context.
<div class="entry">
<h1>{{title}}</h1>
<h2>By {{author.name}}</h2>
<div class="body">
{{body}}
</div>
</div>
That template works with this context
var context = {
title: "My First Blog Post!",
author: {
id: 47,
name: "Yehuda Katz"
},
body: "My first post. Wheeeee!"
};

Backbone and Marionette convention for representing an itemview that is an object and a collection

So I have an object that looks something like this
{
first_name: 'Matt',
last_name: 'Damon',
movies: [{name: 'mov_name1', director: 'Sven'}, {name:'mov_name2', director: 'Joe'}]
}
There is an endpoint that will return this, and I will use fetch to get this data and put it in a model or ItemView. (I have to use fetch because I really want a collection of these objects, so I'll call fetch on the collection.) However, the representation of the object should not be a model alone, it should be a model (or ItemView), and the movies attribute should be a collection, since when I display the row for this object it should be a CompositeView with a model and a collection. My question is how do I convert this object representation to a model and collection after a fetch called on the model?
Sorry it's a bit confusing to explain...
do a fetch on your collection and then for each element create a model and a collection that you can pass then to a compositeView
something like this
mycollection = new MyCollection();
mycollection.fetch();
mycollection.each(function(item){
var actorModel = new ActorModel({firstName : item.get('first_name'),
lastname: item.get('last_name')});
var moviesCollection = new MoviesCollection(item.get('movies'));
var xCompositeView = new XCompositeView({model:xmodel,
collection:moviesCollection});
});
this way your Compositeview gets a model and a collection and you can render them properly using an itemView for each movie.
hope this helps.
If its not a problem to use another framework, there is one called backbone-relational. It is designed to handle the relations between models. See the zoo example in Backbone-Relational
In the example there is a Zoo model and models has a Animal Collection which consist of animal models. With a single fetch on Zoo model, you will have the animal collection as Backbone collection instead of array of plain objects.

Backbone / Underscore chain method with where method

There has to be something simple I am missing here.
http://jsfiddle.net/v9mdZ/
I am just learning Backbone and Underscore/loDash and am trying to get familiar with chain.
I have the following code, which works as expected:
var ids = _.pluck(collection.where({'is_checked':true}), 'id');
I attempted to refactor this, using chain like so:
var ids = collection.chain().where({'is_checked':true}).pluck('id').value();
Why doesn't the refactored code work? Am I using chain wrong?
Solution (details below)
Don't use where with chain.
The merging of some Underscore methods into collections is a little imperfect. When you say collection.some_mixed_in_underscore_method(), the collection unwraps some of the Backbone stuff behind your back so that the Underscore method is applied to the attributes inside the collection's models; it sort of works like this:
var ary = _(this.models).map(function(m) { return m.attributes });
return _(ary).some_mixed_in_underscore_method();
But collection.chain() doesn't work like that, chain just wraps the collection's models directly so if you do this:
console.log(collection.chain());
you'll see that chain is giving you an object that wraps an array of models. Your models won't have an is_checked property (i.e. there is no model.is_checked), they will have is_checked attributes though (i.e. there will be model.get('is_checked') and model.attributes.is_checked).
Now we can see where everything goes wrong:
collection.chain().where({'is_checked':true})
The models don't have is_checked properties. In particular, there won't be any models where is_checked is true and everything after the where is working with an empty array.
Now that we know where things go sideways, how do we fix it? Well, you could use filter instead of where so that you can easily unpack the models:
collection.chain()
.filter(function(m) { return m.get('is_checked') })
.pluck('id')
.value();
But, your models don't have ids yet as you didn't create them with ids and you haven't talked to a server to get ids so you're going to get an array of undefineds back. If you add some ids:
var collection = new App.OptionCollection([
{id: 1, 'is_checked': true},
{id: 2, 'is_checked': true},
{id: 3, 'is_checked': false}
]);
then you'll get the [1,2] that you're looking for.
Demo: http://jsfiddle.net/ambiguous/kRmaD/

Backbone: How to change the order of models

In a collection, how can we change the order of models?
This is not about sorting the models, or writing a comparator function to achieve a sorting order.
When a collection is built from an array of objects, the order of models will be the insertion order.
Now my question is: After the collection is built, how to change the position of the models as per my preference.
For example,
When the collection is built, order of models as per the original array like: 0, 1, 2, 3, 4, 5.
Now, I want to change the order like 2, 4, 0, 1, 3, 5.
In fact it boils down to writing a comparator -- if you want to impose a certain order of the models in the collections, you are in fact sorting them. To achieve what you want it seems easiest to add some order attribute to the model and then write a comparator working on it.
I know this is an old post, but I had the same one and came up with a solution. In short, my solution is to replace the collection.models list with a new list containing the same models but in the order I wanted it to be in... Below is my use case and example (using coffee script).
NOTE: I am not 100% sure this will not be buggy since this changes collection's list rather than sorting the original list in-place. But I have not yet found a failure mode and I've tested several.
My use case was a DOM list that is sortable (via jquery.ui/sortable). I need the collection's order to reflect what is shown on the DOM.
Each sortable DOM element has a data-cid attribute corresponding to the model, I did this in the initialize method of each Backbone.View list item.
class ListItem extends Backbone.View
...
initialize: (options = {}) ->
#$el.attr "data-cid", #model.cid
The parent Backbone.View (an unordered list) listens to a sortupdate event (the user sorted something), then constructs a list of cids with the new order, and passes this into the collection to be sorted.
class List extends Backbone.View
...
tagName: "ul"
events:
"sortupdate" : "sort"
render: ->
#$el.html("") # reset html
#collection.each (model) =>
view = new ListItem model: model
#$el.append view.render().$el
return #
sort: (e, ui) ->
e.stopPropagation()
orderByCid = #$el.children().map(-> $(#).attr "data-cid")
#collection.orderModels orderByCid
The collection makes a new list of models in the same order as the cids using Backbone.Collection::get and then replaces its model list with this.
class DataList extends Backbone.Collection
...
orderModels: (cids = []) ->
# Add validation that all the cids exist in the collection
# and that list and collection are the same length!
result = []
result.push #get(cid) for cid in cids
#models = result

Resources