Converting JSON data to Backbone Model with child Collection - javascript

I am working with a Playlist object which has some properties defining itself as well as a PlaylistItem collection.
When I receive data from my server, I get its JSON response in my client-side success method:
success: function (data) {
console.log("JSON data:", data);
playlists = _.map(data, function (playlistConfig) {
return new Playlist(playlistConfig);
});
...
}
Here, I convert my JSON data into Playlist objects. Each Playlist object is a Backbone.Model.
Here's how my data looks:
And here's what the Playlist constructor looks like:
return function(config) {
var playlist = new Playlist(config);
return playlist;
};
var Playlist = Backbone.Model.extend({
defaults: function() {
return {
id: null,
userId: null,
title: 'New Playlist',
selected: false,
position: 0,
shuffledItems: [],
history: [],
items: Backbone.Collection.extend({
model: PlaylistItem
})
};
},
...
}
My problem:
If I create a Playlist object with defaults, it initializes with an empty Backbone.Collection for PlaylistItem. However, if I create a Playlist object with an already-defined collection, I get a basic array and not a Backbone.Collection. This is because I am working with JSON data from the server which has not been converted to Backbone entities yet. That data is extended over the Playlist's defaults and overwrites the Backbone.Collection entity.
What is a proper way to initialize with a populated Backbone.Collection? I could write code in Initializes which checks the type of my items array and if it is not a Backbone.Collection I could create a new Backbone.Collection and add the items to it and then replace the old array with the new one, but that seems really hoakey.

Don't define your PlalistItems Collection inside defaults, but beforehand.
Then, create an initialize method on your Playlist Model like so:
var PlaylistItems = Backbone.Collection.extend({
...
});
var Playlist = Backbone.Model.extend({
initialize: function() {
this.set('items', new PlaylistItems(this.items));
},
defaults: function() {
return {
id: null,
userId: null,
title: 'New Playlist',
selected: false,
position: 0,
shuffledItems: [],
history: [],
items: [] // don't define your PlaylistItems Collection here
};
}
});
Check out the fiddle here: http://jsfiddle.net/georgedyer/r2XKb/
(you'll need to open the console to see the collection)

Another issue I ran into was after you save your model to the server you will get back a response that will change your embedded collection into a regular javascript array. To remedy this I had to override the parse function on my model class like so:
var model = backbone.Model.extend({
urlRoot : "/rest/model",
initialize: function(){
this.set("myCollection", new MyCollection(this.myArray));
},
defaults: {
myArray: []
},
parse: function(response){
this.set(response);
this.set("myArray", new MyCollection(response.myArray));
}
});

Related

Backbone parse server response to model

I'm trying to deal with a server response, and am a little confused how to turn the json response into Backbone Models.
My Backbone model looks like so:
Entities.Recipe = Backbone.Model.extend({
defaults: {
id: '',
name: '',
introduction: ''
},
parse: function (response)
{
if(._isObject(response.results)){
return response.results
else {
return response
}
})
Entities.RecipeCollection = Backbone.Collection.extend({
url: 'recipes',
model: Entities.Recipe
)}
var API = {
getRecipeEntities: function (){
var recipes = new Entities.RecipeCollection()
var defer = $.Deferred()
recipes.fetch({
url: 'http://3rdpartyApilocation.com/recipes'
success: function (data) {
defer.resolve(data)
}
})
var promise = defer.promise()
$.when(promise).done(function (fetchedData)
{})
return promise
}
RecipeManager.reqres.setHandler('recipe:entities', function()
{
return API.getRecipeEntities()
}
And the response.results is an Array of objects - with each object having an id key, a name key and an introduction key. But because I am so inexperienced with Backbone I have no idea how to map those results to the model?
I have installed Chromes Marionette inspector and when I look at the entire array of results seems to be passed to the model, rather than each individual object within each response.result being set to each individual model. Sorry if I can't be more clear - I'm very new to Backbone...
Perhaps your confusion is because you're in fact able to use parse on a model or on a collection. And from your explanation it looks like the response.results object returns a list of objects that you want to become models in your application. But because you're catching that object in a model, the model doesn't know what to do with that array.
Let's say you have a response like this:
{
"status": "ok",
"results": [
{
"id": 1,
"name": "Pie"
}, {
"id": 2,
"name": "Rice"
}, {
"id": 3,
"name": "Meatballs"
}
]
}
Then you would just use parse on your Collection to let it know the response isn't array itself, and help it find it in the results property.
Here's a working example:
var Recipe = Backbone.Model.extend();
var Recipes = Backbone.Collection.extend({
model: Recipe,
url: 'http://www.mocky.io/v2/56390090120000fa08a61a57',
parse: function(response){
return response.results;
}
});
var recipes = new Recipes();
recipes.fetch().done(function(){
recipes.each(function(recipe){
/** Just demo of the fetched data */
$(document.body).append('<p>'+ recipe.get('name') +'</p>');
});
});
<script src='http://code.jquery.com/jquery.js'></script>
<script src='http://underscorejs.org/underscore.js'></script>
<script src='http://backbonejs.org/backbone.js'></script>

How to get data out of ember objects

I'm fairly new to ember and I'd like to know whats the fastest way to extract the data out of ember objects. I've loaded my model with a very large amount of records using this.store.find('modelName);` in my route.
I created a component on my view using {{kendo-ui.kendo-table descriptor=tableDescriptor data=model}}. My controller defined other arguments to be passed to my component (descriptor).
In my components.js I'm' getting the data passed over by using
export default Ember.Component.extend({
didInsertElement: function() {
var columns = this.get('descriptor.columns'); // this is right
var model = this.get('data')['content']; // this returns the objects of the model
var height = this.get('descriptor.height'); // this is ok too
Ember.$('#kendo-table').kendoGrid({
dataSource: {
data: model,
pageSize: 100
},
height: height,
scrollable: {
virtual: true
},
groupable: true,
sortable: true,
columns: columns
});
}
});
On the line var model = this.get('data')['content'];, this gives me an Array of Ember Classes. Inside each class, there is a _data object that holds the value of the actual class.
The easiest solutions is to just loop through and extract the _data but that is no good for larger model array. Is there a quick way to extract all the _data from my array of ember objects?
You could use getProperties method. http://emberjs.com/api/classes/Ember.Object.html#method_getProperties
To get the values of multiple properties at once, call getProperties with a list of strings or an array:
record.getProperties('firstName', 'lastName', 'zipCode');
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
You could define computed property dataArray:
dataArray: function() {
return this.get('data').map( function(item) {
return item.getProperties('id', ... ); // your list of properties
});
}.property('data.[]'),
didInsertElement: function() {
//...
Ember.$('#kendo-table').kendoGrid({
dataSource: {
data: this.get('dataArray'),
//...
},
// ...
});
}
UPDATE:
for records (DS.Model) you could use toJSON method. Use DS.JSONSerializer to get the JSON representation of a record.
toJSON takes an optional hash as a parameter, currently supported options are:
includeId: true if the record's ID should be included in the JSON representation.
http://emberjs.com/api/data/classes/DS.Model.html#method_toJSON

How can I fake Collection data?

What I mean by this is I want to create it artificially.
This is for testing purposes.
But for models, it is quite simple. I just set defaults I instantiate the model object and from there I can use this.model.toJSON() to grab the created data.
I want to use this same trick with collections. Is there a similar way to do this with collections? What I would want to do is have the collection create x ( 8 in this case ) copies of Model defaults.
Basically what I was doing before for models but a little bit more complex as it applies to Collections.
Here is the actual use case. It should be simple.
/**Model
**/
// name, picture, time, tweet, h_file
var FeedRow = Backbone.Model.extend({
Name: 'FeedRow',
defaults: {
name: "default",
picture: 0,
time: "0",
tweet: "default",
h_file: "default"
}
});
/**Collection
**/
var FeedTable = Backbone.Collection.extend({
Name: 'FeedTable',
model: FeedRow
});
When your FeedTable collection is constructed you could set the model on it multiple times in the initialize method.
var FeedTable = Backbone.Collection.extend(
{
Name: 'FeedTable',
model: FeedRow,
initialize: function()
{
model = this.model;
models = [];
_.times(8, function(n)
{
models.push(new model({id: (n + 1)}));
});
this.set(models);
}
});

how to call the collection function in model ( backbone) for amd architecture?

// file one
// how to call the choicesCollection setnextOne in ChoicesModel default function(object)
var ChoicesModel = Backbone.Model.extend({
defaults: function() {
// this.collection ??
return {
seq_id: choicesCollection.setnextOne(),
subject: ""
};
},
initialize: function() {
console.log(this);
if (!this.get("seq_id")) {
this.set({"seq_id": this.defaults().seq_id});
}
}
});
// file two
var ChoicesCollection = Backbone.Collection.extend({
model:ChoicesModel,
setnextOne: function() {
if (!this.length) return 0;
return +this.last().get('seq_id') + 1;
},
// sort
comparator: function(choice) {
return choice.get('seq_id');
}
});
// file three
var choicesCollection = new ChoicesCollection();
Giving some more insight into Manikandan's answer...
If you look at the backbone code you'll see the following (I've removed some bits):
var View = Backbone.View = function(options) {
options || (options = {});
_.extend(this, _.pick(options, viewOptions));
};
// List of view options to be merged as properties.
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
As you can see_.pick is used to white list a sub-set of properties from the options passed in. The View prototype is then extended to bolt on these properties (if they've been passed).
In short, the following properties are automatically thrown onto your view if you pass them as options:
model, collection, el, id, attributes, className
As per the backbone documentation you should access the collection by this.collection once you added model to collection. Or you need to send collection option when you create a model.

Backbone.js: Passing value From Collection to each model

I need to pass a value from the view to each models inside a collection, while initializing.
Till Collection we can pass with 'options' in the Backbone.Collection constructor.
After this, is there any technique where I can pass the some 'options' into each models inside the collection?
var Song = Backbone.Model.extend({
defaults: {
name: "Not specified",
artist: "Not specified"
},
initialize: function (attributes, options) {
//Need the some_imp_value accessible here
},
});
var Album = Backbone.Collection.extend({
model: Song
initialize: function (models, options) {
this.some_imp_value = option.some_imp_value;
}
});
You can override the "_prepareModel" method.
var Album = Backbone.Collection.extend({
model: Song
initialize: function (models, options) {
this.some_imp_value = option.some_imp_value;
},
_prepareModel: function (model, options) {
if (!(model instanceof Song)) {
model.some_imp_value = this.some_imp_value;
}
return Backbone.Collection.prototype._prepareModel.call(this, model, options);
}
});
Now you can look at the attributes passed to the model in 'initialize' and you'll get some_imp_value, which you can then set on the model as appropriate..
While it appears to be undocumented, I have found that in at least the latest version of backbone (v1.3.3) that the options object passed to a collection gets passed to each child model, extended into the other option items generated by the collection. I haven't spent the time to confirm if this is the case with older releases.
Example:
var Song = Backbone.Model.extend({
defaults: {
name: "Not specified",
artist: "Not specified"
},
initialize: function (attributes, options) {
//passed through options
this.some_imp_value = options.some_imp_value
//accessing parent collection assigned attributes
this.some_other_value = this.collection.some_other_value
},
});
var Album = Backbone.Collection.extend({
model: Song
initialize: function (models, options) {
this.some_other_value = "some other value!";
}
});
var myAlbum = new Album([array,of,models],{some_imp_value:"THIS IS THE VALUE"});
Note: I am unsure if the options object is passed to subsequent Collection.add events

Categories

Resources