Parsing nested collections in backbone - backbone.js

Hi i have a nested collection.
{"Categories":[{},{},{}],"Preference":[{},{},{}],"Users":[{},{},{}]}
Now what i what is to pass Categories to another collection, preference to another collection and users to another collection too. Is it still confusing? I've read that you can do this on a model but will it do the work? Any ideas?

If you have control over your backend, what you probably want to do is split the retrieval of the collection into three different Ajax calls:
var Categories = Backbone.Collection.extend({
url: "/categories"
});
var Preferences = Backbone.Collection.extend({
url: "/preferences"
});
var Users = Backbone.Collection.extend({
url: "/users"
});
var categories = new Categories();
categories.fetch();
...
If you can't change the routes on your server for some reason, then you can manually create the models by passing the arrays into the constructor:
var categories = new Categories(data["Categories"]);
If the data is in JSON format you can use jQuery to parse into a Javascript object:
jQuery.parseJSON(data);

Related

Backbone, requirejs, multiple views, one collection

Below you can see four basic requireJS files. How can I have multiple Backbone.js views that will all share one collection which has been initiated and fetched elsewhere?
Please note
I am aware I can pass the collection in App.js however I would like to refrain from doing so since I will probably have many collections that will need to be used in many views, and I don't want to pass each of them in App.js.
Collection.js
return Backbone.Collection.extend({
url: '...'
});
App.js
var collection = new Collection();
$.when(collection.fetch()).done(function(){
new View1();
new View2();
});
View1.js
define(['Collection'], function(Collection){
return Backbone.View.extend({
initialize: function(){
console.log('Need the initiated, fetched collection here...');
});
});
});
View2.js
define(['Collection'], function(Collection){
return Backbone.View.extend({
initialize: function(){
console.log('Need the initiated, fetched collection here...');
});
});
});
Quick answer: RequireJS runs function body code once and the return statement alone multiple times. You can, hence, create a Collection Instance and return that to every person who requires it.
Collection.js
var CollectionConstructor = Backbone.Collection.extend({
url: '...'
});
var CollectionInstance = new CollectionConstructor();
return CollectionInstance;
Alternatively, if you want more than one instance of CollectionInstance running around, and don't want to pass it to the views, I don't believe anything short of global variables will help. That would look like:
Globals.js
var AppGlobals = AppGlobals || {};
AppGlobals.oneSet = new Collection ();
AppGlobals.anotherSet = new Collection ();
You can now have your views depend on Globals.js and access them from here. Depending on your use, either of these two should work. Keep in mind that in the second approach, your Collection.js is unmodified.

How does Chaplin.js handle passing a collection to a view?

I can create a simple model like so:
define(["models/base/model"], function(Model) {
"use strict";
var IssueModel = Model.extend({
defaults:{
lastName: "Bob",
firstName: "Doe"
}
});
return IssueModel;
});
And then from my controller I can do this:
this.model = new IssueModel();
And then when I create my view I can pass it my model like so:
this.view = new IssueView({model: this.model});
Finally, in my template I can successfully get properties on the model by doing this:
Hi {{firstName}} {{lastName}}
But when I define a collection using IssueModel and I try to pass the collection to my view (and not the model like I showed previously) I can't figure out how to reference the models in my Handlebars template:
var self = this;
this.collection = new IssueCollection();
this.collection.fetch({
success: function(collection) {
self.view = new IssueView({collection: collection});
console.log(self.collection);
},
error: function(collection, error) {
// The collection could not be retrieved.
}
});
I know fetch properly retrieves 5 models from my Parse.com backend because this is what I get on the console:
My question is this. I know Chaplin.js uses getTemplateData, but when I pass a model I don't have to do anything special in order to reference the properties in my view. How would I reference, specifically iterate, over the collection I passed to my view in my Handlebars template?
{{#each [Model in the collection I passed to the view]}}
{{title}}
{{/each}}
Chaplin will render a collection using a CollectionView, it's basicly an extention of a normal view that listens for changes in your collection and adds/removes subviews accordingly.
this.view = new IssueCollectionView({collection: this.collection});
Also there is no need to wait for success call when using a collection view since it will automaticly render every child item when data is added.

Is there an equivalent to parse to use before syncing with the server?

In my backbone model, I parse the response from the server:
var MyModel = Backbone.Model.extend({
urlRoot: "/users",
parse: function(response){
var data = {};
data.id = reponse.userDetails.id;
data.name = response.userDetails.firstname + " " + response.userDetails.lastname;
data.description = response.userDetails.description;
return data;
}
});
var myModel = new MyModel({id: 1});
myModel.fetch();
The views that use this model can manipulate it, for example, if the user were to click on the view to "select" it, it would update the model...
myModel.set({selected: true});
...and the view would re-render based on the model's change event and highlight the "selected" user.
When it comes time to save the model to the server, how do I only send the attributes the server wants? and ignore the attributes which were added through user interaction.
OR
Should the data model always reflect what the server returns? If so, is there a better way to store the user interactions (whether the view is "selected")? Should it be a separate model than the actual data model?
Thanks
The model doesn't need to mirror the data on the server if that doesn't make sense for your application.
For the model's attributes, if you don't need to render those attributes in a template, then you can just override model.toJSON() to only serialize the attributes you want sent to the server. Be careful though, in this case if you are rendering your template (or anything else) using this.model.toJSON() then it will also be affected. If that's a problem then you can override model.sync() instead and manipulate the data passed in before sending it to Backbone.sync. For example:
var myModel = Backbone.Model.extend({
sync: function (method, model, options) {
// remove the unwanted attributes. Something like...
options.attrs = _.pick(model.attributes, 'attribute1', 'attribute2', 'attribute3');
return Backbone.sync.call(this, method, model, options);
}
});
Overriding model.toJSON as suggested by mu_is_too_short worked nicely for me.
In the model
function() {
var json = Backbone.Model.prototype.toJSON.call(this);
json.ExtendedFieldData = JSON.stringify(json.ExtendedFieldData);
return json;
},
We use model.attributes for templates.

backbone.js - how can i fetch local or server data

I am very new to backbone.js, to try it out I made a function to append an element using my array. But I don't know how to fetch the same from server or my local path.
I tried with some tutorial but still I couldn't get any good result. Can any one correct my function to fetch the data from local folder or server?
code :
this is my local path: '..data/data.json'
(function($){
var student = [
{name:'student1'},
{name:'student2'},
{name:'student3'}
]
var model = Backbone.Model.extend({
defaults:{
name:'default name'
}
});
var collection = Backbone.Collection.extend({
model:model
});
var itemViews = Backbone.View.extend({
tagname:'li',
render:function(){
this.$el.html(this.model.get('name'));
return this;
}
})
var view = Backbone.View.extend({
el: $("#contacts"),
initialize:function(){
this.collection = new collection(student);
this.render();
},
render:function(){
var that = this;
_.each(this.collection.models, function(item){
that.renderName(item);
})
},
renderName:function(item){
var itemView = new itemViews({model:item});
this.$el.append(itemView.render().el);
}
});
Collections have the url property which links to the url where the models can be fetched (in a proper JSON format of course).
If you have a RESTful API you can tie them directly to a collection on your backend.
In your case you can modify the collection definition as follows
var collection = Backbone.Collection.extend({
model:model,
url: '..data/data.json' //OR WHATEVER THE URL IS
});
Then when instantiating your collection you have to call the fetch() method to fill in the collection.
myCollection = new collection();
myCollection.fetch();
For more information see this post
Backbone will call GET, POST, DELETE, etc on that same url depending on what needs to be sent or received from the server. So if you are building the REST api, your controller or router should route the appropriate functions to those methods. See below for exact mapping:
create → POST /collection
read → GET /collection[/id]
update → PUT /collection/id
delete → DELETE /collection/id
As a side note, if you have no control over the JOSN that is returned from the server, you can format it in any format you like in the parse function.
parse: function(response) { return response.itemYouWantAdded; }
This would be the case if your server is returning an object within an object, such as twitter feeds.
Or you could simply populate the model from the response manually in the parse function
parse: function(response) { var tmpModel = {
item1:response.item1,
item2:response.item2
};
return tmpModel; }
Hope this helps.
You can set a URL for your model or for your collection. Then when you run fetch it will read your JSON straight into either a single model or a collection of models. So in your case, your collection might look like
var collection = Backbone.Collection.extend({
url: '..data/data.json',
model: model
});
collection.fetch(); // Could also use collection.reset()
You just need to make sure your JSON is formatted properly to match your model's attributes.

Backbone.js - fetch data and display

I'm just beginning out on Backbone.js. Here's my code.
$(function(){
//Backbone Model
var Cat = Backbone.Model.extend({});
// create a collection
var CatCollection = Backbone.Collection.extend({
model: Cat,
url: 'http://localhost/cats/index.php/cats/index'
});
var catCollection = new CatCollection();
catCollection.fetch();
// Backbone view
var CatView = Backbone.View.extend({
el: $("#contents"),
initialize: function() {
this.render();
},
render: function() {
this.el.html(catCollection);
}
});
var catView = new CatView();
});
What I am doing is.
Create a backbone model
Create a collection using the model I created.
Fetch data from MySQL database - this returns a JSON data object.
Display the fetched data in the div "#contents".
On google Chrome, I can see that the "fetch()" method works, because I can see my JSON object returned as
[{"id":"1","name":"stella","age":"5"},{"id":"2","name":"Max","age":"2"}]
But if I do "alert(catCollection)" after the fetch, it displays "[object] [object]".
What is the best way of displaying this?
You should use the templates in JST array.
$(this.el).html($(JST["comments/item"](model.toJSON())));
"comments\item" is the template path and name
If you are using Rails, just use the Jammit and write templates with ERB (default) or Jade
The backbone.js documentation give an example like so:
alert(JSON.stringify(catCollection));
http://backbonejs.org/#Collection-toJSON
Use console.log.

Resources