Get Items in Backbone Collection - collections

First - I'm new to Backbone, so bear with the possibly stupid nature of this question. I have a model:
var AppModel = Backbone.Model.extend({
defaults: {
mynumber: null
}
});
I can successfully add items dynamically to the collection (console.log shows that they're going in fine), but I'm just not sure how to pull them out, here's what I'm trying:
printCollection: function(){
this.collection.each(function(item){
console.log(item.mynumber);
});
}
Which shows the number of items and undefined.

You need to use the attribute accessor, not the value.
item.get('mynumber');

Related

Need suggestions to handle view updates

In an application currently I am loading my views using routers like below
router('menu/:item', function (item) {
app.uiHandler.toggleMenuSelected('menu', item);
// The below method updates the view with selected menu item's model.
app.channel.publish('menu', item);
});
Currently each menu item shares same data object. But the master view is replaced with new html based on each menu selection.
I am thinking to have instance like below for each menu item
var ractive = new Ractive({
el: 'container', // el is same for all instances.
template: '<p> I am {{selection}}, after {{prevSelection}}!</p>',
data: { selection: 'Home', prevSelection: 'Profile' }
});
But here my doubt is as I will be invoking each instance to render the view into 'container' whenever hash is changed how to clear all the two way data bindings created when master view is replaced with new html. Please help me on this.
If I am handling in wrong way, what would be the best way to handle the same.
Note : My question might sound like stupid, but I am looking for clarification on this :)
how to clear all the two way data bindings created when master view is replaced with new html
You can use teardown() to destroy the ractive instance, but if you don't ractive will do that automatically as soon as you try to render a new instance into the same container.
That said, it's probably better to have one instance and only update the data. I.e. when the section changes call ractive.set({ selection: 'Profile', prevSelection: 'Home' }).

EmberJS View programmatically using a not loaded model

I have a model:
App.Checkin = DS.Model.extend({
latitude: DS.attr('string'),
longitude: DS.attr('string'),
time: DS.attr('number')
});
And a route that loads the collection of checkin models (making a request with ember-data) by
model: function() {
return this.store.find('checkin');
}
And then in the template for the route I have
{{view App.MapView}}
And I need to access the model programmatically so that I can iterate over each item in the model to add a pin to the map.
Inside my view I have
didInsertElement: function() {
var data = this.get("context.content");
}
and data is
Class {type: function, store: Class, isLoaded: true, isUpdating: true, toString: function…}
In the network window, the request to the server hasn't completed by that point, so it obviously wouldn't have data to provide. (Even if it did, I don't know how to query an object like that, none of the expected methods worked (get/forEach))
I believe that I need to observe the model being changed, and have tried
updatePins: function() {
debugger;
}.observes('context.content')
inside of the view. I have tried binding to all sorts of things, but reRender has never been called. I've tried the recommendations on Emberjs view binding for Google maps markers
by trying to bind to controller.content, context, App.Checkin, etc.
How do I go about getting the data from the model once it has loaded...inside of the view?
Until the model does not resolve what you get is a promise for that model, obviously the promise does not contain the data just yet but when the request comes back from the server. You could check in your template if the model has data, by observing the model.length property for example using an if helper, the if helper block will re-evaluate when the length property changes this beeing when it has received data.
For example, you could try something like this:
...
{{#if model.length}}
{{view App.MapView}}
{{/if}}
...
This will ensure that your App.MapView is then rendered when your model has data and therefore you will have also access to the data in the view's didInsertElement hook as you'd expect.
Update
Your reRender hook is named slightly wrong, it should be rerender
rerender: function() {
debugger;
}.observes('context.content')
Hope it helps.

Knockout 2 way foreach binding DOM changes update Model

If I have a list on a page, and it is using knockout's foreach binding to display list items, then something else updates the DOM to add an extra list item. If there any way I can get knockout to detect that DOM change and update its model to add the new item to the observableArray?
Here is a fiddle which shows the problem...
http://jsfiddle.net/BGdWN/1/
function MyViewModel() {
this.items = ko.observableArray([
{ name: 'Alpha' }, { name: 'Beta' }, { name: 'Gamma' }, { name: 'Delta' }
]);
this.simpleShuffle = function() {
this.items.sort(function() {
return Math.random() - 0.5; // Random order
});
};
this.simpleAdd = function() {
$("#top").append("<li>New item</li>");
}
}
ko.applyBindings(new MyViewModel());
It has 2 lists bound to the same observableArray, click the addItem button and you can see that the DOM is updated to include the new list item in the top list, but I would like the second list to be updated too, all via the model.
It seems that knockout ignores DOM elements that it didnt render, you can see this by clicking the shuffle button, it leaves the new items there. I would have expected it to remove them and do a full re-render.
Please don't answer with "Just add the item to the observableArray"
Take a look at the first link and the second link Interface MutationEvent
See Fiddle
$('#top').bind('DOMNodeInserted DOMNodeRemoved', function () {
alert('Changed');
});
I hope it helps.

Many-to-Many Ajax Forms (Symfony2 Forms)

I have a many-to-many relationship in mongodb between Players and Tournaments.
I want to be able to add many Players to a Tournament at once. This is trivial to do without ajax, but we have a DB of thousands of Players, and so the form select becomes huge.
We want to use ajax for this. Is it possible to create a single widget (with js) to handle this properly? If so, any hints on what jquery plugin (or other) to use?
If not, whats the standard strategy to do this? I suppose I could heavily change the view for this form and use an ajax autocomplete to add one player at a time, and then some more code to delete each player one at a time. However, I'd really like to have a single widget I can re-use because its so much cleaner and seems much more efficient.
I have been playing with Select2 all day (similar to jQuery Chosen) and I have it working for adding many Players via ajax, but it does not allow me to set the already attached players when I initially load the page, so I won't be able to see who's already in the tournament and would have to retype everyone in.
Thanks for ANY input on this matter! I can't find anything via Google.
I was able to accomplish this by $.ajax after the constructor within the onload function where //website/jsonItem is a json-encoded list of all items, and //website/jsonItemUser is a json-encoded list of all items attached to user. I used // to keep the https/http consistent between calls.
$(document).ready(function(){
$('.selectitem').select2({
minimumInputLength:0
,multiple: true
,ajax: {
url: "//website/jsonItem"
,dataType: 'jsonp'
,data: function (term, page) {
return {
q: term, // search term
limit: 20,
page: page
};
}
,results: function (data, page) {
var more = (page * 20) < data.total;
return {
results: data.objects, more: more
};
}
}
,initSelection: function(element, callback){
var items=new Array();
$.ajax({
url: "//website/jsonItemUser"
});
callback(items);
}
});
$.ajax({
url: "//website/jsonItemUser"
,dataType: 'jsonp'
,success: function(items, status, ob) {
$('.selectitem').select2('data',items);
}
});
});

Render a Backbone.js collection

I am a Backbone.js n00b and trying to get my head around it. I know how to render a model using a view and the built-in underscore.js templating engine. Now I'm trying to render a collection and that's where I get stuck. There is no server here, so I'm not fetching anything remotely, just a simple HTML page with some JavaScript.
ContinentModel = Backbone.Model.extend({});
ContinentsCollection = Backbone.Collection.extend({
model: ContinentModel,
initialize: function () {
this.continentsView = new ContinentsView;
this.bind("reset", this.continentsView.render);
}
});
ContinentsView = Backbone.View.extend({
el: '#continents',
template: _.template($('#continents-template').html()),
render: function() {
var renderedContent = this.template(this.collection.toJSON());
$(this.el).html(renderedContent);
return this;
}
});
$(function() {
var continentsCollection = new ContinentsCollection();
continentsCollection.reset([{name: "Asia"}, {name: "Africa"}]);
});
It breaks on the template attribute line in the view but I'm not sure that's where I need to look. Am I supposed to render a collection or do I miss the point completely here (maybe collections are just grouping objects and I shouldn't look at it as a list I can render)?
Thanks for helping...
The problem is that when you define ContinentsView, the template is evaluated and it uses $('#continents-template') - but the DOM is not ready yet, so it does not find the template.
To solve it, simply move the template assignment in the initialize function:
ContinentsView = Backbone.View.extend({
el: '#continents',
initialize: function() {
this.template = _.template($('#continents-template').html());
}
...
Regarding collections, yes, they are grouping objects, specifically sets of models.
You should make the code so the models (and collections) do NOT know about the views, only the views know about models.
ContinentModel = Backbone.Model.extend({});
ContinentsCollection = Backbone.Collection.extend({
model: ContinentModel,
// no reference to any view here
});
ContinentsView = Backbone.View.extend({
el: '#continents',
initialize: function() {
this.template = _.template($('#continents-template').html());
// in the view, listen for events on the model / collection
this.collection.bind("reset", this.render, this);
},
render: function() {
var renderedContent = this.template(this.collection.toJSON());
$(this.el).html(renderedContent);
return this;
}
});
$(function() {
var continentsCollection = new ContinentsCollection();
continentsCollection.reset([{name: "Asia"}, {name: "Africa"}]);
// initialize the view and pass the collection
var continentsView = new ContinentsView({collection: continentsCollection});
});
It is also worth noting there are additional complexities that quickly rear their heads when rendering a collection in a view. For instance, the view generally needs to be re-rendered when models are added or removed from the collection. It isn't rocket science to implement your own solution, but it is probably worth looking into existing solutions since there are quite a few tried and tested ones out there.
Backbone.CollectionView is a robust collection view class that handles selecting models in response to mouse clicks, reordering the collection based on drag and drop, filtering visible models, etc.
Several popular frameworks built on top of backbone also provide simple collection view classes, like Backbone.Marionette, Chaplin, and Layout Manager.
Even though Backbone itself does not provide any structure for rendering a collection, it is a non-trivial problem and lots of people have different opinions on how it should be done. Luckily it is such a common need that there are quite a few good options already in the eco system.

Resources