Backbone multiple collections fetch from a single big JSON file - json

I would like to know if any better way to create multiple collections fetching from a single big JSON file. I got a JSON file looks like this.
{
"Languages": [...],
"ProductTypes": [...],
"Menus": [...],
"Submenus": [...],
"SampleOne": [...],
"SampleTwo": [...],
"SampleMore": [...]
}
I am using the url/fetch to create each collection for each node of the JSON above.
var source = 'data/sample.json';
Languages.url = source;
Languages.fetch();
ProductTypes.url = source;
ProductTypes.fetch();
Menus.url = source;
Menus.fetch();
Submenus.url = source;
Submenus.fetch();
SampleOne.url = source;
SampleOne.fetch();
SampleTwo.url = source;
SampleTwo.fetch();
SampleMore.url = source;
SampleMore.fetch();
Any better solution for this?

Backbone is great for when your application fits the mold it provides. But don't be afraid to go around it when it makes sense for your application. It's a very small library. Making repetitive and duplicate GET requests just to fit backbone's mold is probably prohibitively inefficient. Check out jQuery.getJSON or your favorite basic AJAX library, paired with some basic metaprogramming as following:
//Put your real collection constructors here. Just examples.
var collections = {
Languages: Backbone.Collection.extend(),
ProductTypes: Backbone.Collection.extend(),
Menus: Backbone.Collection.extend()
};
function fetch() {
$.getJSON("/url/to/your/big.json", {
success: function (response) {
for (var name in collections) {
//Grab the list of raw json objects by name out of the response
//pass it to your collection's constructor
//and store a reference to your now-populated collection instance
//in your collection lookup object
collections[name] = new collections[name](response[name]);
}
}
});
}
fetch();
Once you've called fetch() and the asyn callback has completed, you can do things like collections.Menus.at(0) to get at the loaded model instances.

Your current approach, in addition to being pretty long, risks retrieving the large file multiple times (browser caching won't always work here, especially if the first request hasn't completed by the time you make the next one).
I think the easiest option here is to go with straight jQuery, rather than Backbone, then use .reset() on your collections:
$.get('data/sample.json', function(data) {
Languages.reset(data['Languages']);
ProductTypes.reset(data['ProductTypes']);
// etc
});
If you wanted to cut down on the redundant code, you can put your collections into a namespace like app and then do something like this (though it might be a bit too clever to be legible):
app.Languages = new LanguageCollection();
// etc
$.get('data/sample.json', function(data) {
_(['Languages', 'ProductTypes', ... ]).each(function(collection) {
app[collection].reset(data[collection]);
})
});

I think you can solve your need and still stay into the Backbone paradigm, I think an elegant solution that fits to me is create a Model that fetch the big JSON and uses it to fetch all the Collections in its change event:
var App = Backbone.Model.extend({
url: "http://myserver.com/data/sample.json",
initialize: function( opts ){
this.languages = new Languages();
this.productTypes = new ProductTypes();
// ...
this.on( "change", this.fetchCollections, this );
},
fetchCollections: function(){
this.languages.reset( this.get( "Languages" ) );
this.productTypes.reset( this.get( "ProductTypes" ) );
// ...
}
});
var myApp = new App();
myApp.fetch();
You have access to all your collections through:
myApp.languages
myApp.productTypes
...

You can easily do this with a parse method. Set up a model and create an attribute for each collection. There's nothing saying your model attribute has to be a single piece of data and can't be a collection.
When you run your fetch it will return back the entire response to a parse method that you can override by creating a parse function in your model. Something like:
parse: function(response) {
var myResponse = {};
_.each(response.data, function(value, key) {
myResponse[key] = new Backbone.Collection(value);
}
return myResponse;
}
You could also create new collections at a global level or into some other namespace if you'd rather not have them contained in a model, but that's up to you.
To get them from the model later you'd just have to do something like:
model.get('Languages');

backbone-relational provides a solution within backbone (without using jQuery.getJSON) which might make sense if you're already using it. Short answer at https://stackoverflow.com/a/11095675/70987 which I'd be happy to elaborate on if needed.

Related

Manipulate data from angular JSON response

I have a standard JSON response in an Angular controller, which returns data.
I am trying to get specific parts of that data, and manipulate it and use the manipulated version within the code.
Currently i have:
$http.get('/json/file.json').success(function(data) {
$scope.results = data;
});
In the JSON, i have data such as this:
"hotels":[
{
"region": "Indian Ocean"
}
]
In my code, i am using ng-repeat to call "hotel in results.hotels" and using "hotel.region".
How do i grab the hotel.region from the data, and remove the space between the words, replace the space with a '_' and make it all lower case so i end up with "indian_ocean". As well as this, how would i then use this within my ng-repeat?
Many thanks..
data.hotels[0].region.replace(" ","_").toLowercase()
Just do...
$scope.results.forEach(function (element) {element.replace(" ","_").toLowercase()});
I figured it out so this can be used in a more general way.
Create a new filter...
app.filter('removeSpaces', function () {
return function (text) {
var str = text.replace(/\s+/g, '_');
return str.toLowerCase();
};
});
Then this can be used site-wide by calling "{{hotel.region | removeSpaces}}".
Thanks to the people who did respond and for their help.

Loading and using enum values in Ember

I have an Ember app consuming a rails based webservice.
On the Rails side, I have some enums, they are simply arrays.
Now, I would like to retreive those enums in the Ember app, and render them for select values.
The webservice returns a JSON response :
get '/grades.json'
{"grades":["cp","ce1","ce2","cm1","cm2"]}
On the Ember side, I created a GradesRoute like this :
App.GradesRoute = Ember.Route.extend({
model: function () {
return Em.$.getJSON('api/v1/grades.json')
}
}));
Then, I think I need it in the controllers where these enums are in use:
App.StudentsController = Ember.ArrayController.extend({
needs: ['grades'],
grades: Ember.computed.alias('controllers.grades')
}
));
So at least I thought I could iterate over the grades in the students template.
{{#each grade in grades}}
{{grade}}
{{/each}}
But I get no output at all... debugging from the template and trying templateContext.get('grades').get('model') returns an empty array []
Any idea on how I could load and access this data ?
So I ended up with ApplicationRoute, which is the immediate parent of StudentsRoute, so needs is relevant in this case.
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
Em.$.getJSON('api/v1/enums.json').then(function(data){
controller.set('grades', data['grades']);
controller.set('states', data['states']);
}
}
});
Now I can create an alias for each enums I need to use accross my app.
App.StudentsController = Ember.ArrayController.extend({
needs: ['application'],
grades: Ember.computed.alias('controllers.application.grades'),
states: Ember.computed.alias('controllers.application.states')
});
I'm still not confident enough to be sure this is the way to go, any suggestion is welcome !
You just have some of your paths mixed up. In StudentsController, controllers.grades refers to the actual controller, not it's model. The following code should clear things up as it's a bit more explicit in naming.
App.StudentsController = Ember.ArrayController.extend({
needs: ['grades'],
gradesController: Ember.computed.alias('controllers.grades'),
grades: Ember.computed.alias('gradesController.model.grades')
});
Also, be aware that using needs only works if your grades route is a direct parent of your students route. If it's not a direct parent, you won't get back the data you want.

Knockout: nested Elements as observable

I'm trying to build an complexe view with knockout.js and have a few problems..
The content data for the view model is loaded over ajax as JSON. The JSON is quite complex and has multiple nested objects from which some should be observable and others not.
Here is a little example (real one is some levels deeper)
{
BaseData:{
Title:'BaseDataTitle',
DataArray:[{Title:'obs1'}],//this should be observable
SecondArray:[{Title:'notobs1'}],//this should not be observable
},
DataArray:[{Title:'obs1'}]
}
http://jsfiddle.net/wPs7e/
is there any possibility to do that with knockout?
thanks for your help!
You'll have to do some work with your objects...you could use the mapping plug-in, however you'll still need to create some objects to receive your data and conditionally apply the bindings...
var KOObject = function(rawdata) {
var self = this;
self.BaseData = new BaseDataObject(rawdata.BaseData);
self.DataArray = rawdata.DataArray;
}
var BaseDataObject = function(baseData) {
var self = this;
self.Title = baseData.Title;
// you could keep chaining the creation of objects with this concept
self.DataArray = ko.observableArray(baseData.DataArray);
self.SecondArray = ko.observableArray(baseData.SecondArray);
}
before you apply your bindings, new up this object
ko.applyBindings(new KOObject(rawdata));
The mapping plugin is a great idea for automating both the serialization and deserialization of data with knockout...it's just not always necessary if you have the option of initializing your data sets manually...it's good practice anyways.

how to send the data in Json structure

I have a rest service for which I am sending the Json data as ["1","2","3"](list of strings) which is working fine in firefox rest client plugin, but while sending the data in application the structure is {"0":"1","1":"2","2":"3"} format, and I am not able to pass the data, how to convert the {"0":"1","1":"2","2":"3"} to ["1","2","3"] so that I can send the data through application, any help would be greatly appreciated.
If the format of the json is { "index" : "value" }, is what I'm seeing in {"0":"1","1":"2","2":"3"}, then we can take advantage of that information and you can do this:
var myObj = {"0":"1","1":"2","2":"3"};
var convertToList = function(object) {
var i = 0;
var list = [];
while(object.hasOwnProperty(i)) { // check if value exists for index i
list.push(object[i]); // add value into list
i++; // increment index
}
return list;
};
var result = convertToList(myObj); // result: ["1", "2", "3"]
See fiddle: http://jsfiddle.net/amyamy86/NzudC/
Use a fake index to "iterate" through the list. Keep in mind that this won't work if there is a break in the indices, can't be this: {"0":"1","2":"3"}
You need to parse out the json back into a javascript object. There are parsing tools in the later iterations of dojo as one of the other contributors already pointed out, however most browsers support JSON.parse(), which is defined in ECMA-262 5th Edition (the specification that JS is based on). Its usage is:
var str = your_incoming_json_string,
// here is the line ...
obj = JSON.parse(string);
// DEBUG: pump it out to console to see what it looks like
a.forEach(function(entry) {
console.log(entry);
});
For the browsers that don't support JSON.parse() you can implement it using json2.js, but since you are actually using dojo, then dojo.fromJson() is your way to go. Dojo takes care of browser independence for you.
var str = your_incoming_json_string,
// here is the line ...
obj = dojo.fromJson(str);
// DEBUG: pump it out to console to see what it looks like
a.forEach(function(entry) {
console.log(entry);
});
If you're using an AMD version of Dojo then you will need to go back to the Dojo documentation and look at dojo/_base/json examples on the dojo.fromJson page.

parse urls in Backbone.js

I am new to Backbones.js, and I was trying to get my JSON urls and parse them correctly.
This is my code:
window.Post = Backbone.Model.extend({
initialize: function(options) {
this.id = options.id;
},
url: function() {
return 'api/get_post/?post_type=movies&id=' + this.id;
},
parse : function(response) {
return response.posts;
},
});
window.Posts = Backbone.Collection.extend({
model: Post,
defaults: {
model: Post,
},
url: "api/get_recent_posts/?post_type=movies",
parse : function(response) {
return response.posts;
},
});
It seems that parsing for both overrides each other or something. when I remove the parse option from the Post class, I get a full response from the collection, but not from the model.
Are there any clear examples on how to set parsing for different son hierarchies? my JSON result have a status ok before it dives into the actual data.
I've never used bones.js but maybe these examples will help.
I think what you want to do is get rid of the parse() function in your collection. This assumes that since it is a Post collection, your data will come in as an array of Post JSON objects [{id:'1', 'sub':{data}},{id:'2', 'sub':{data}},{id:'3', 'sub':{data}}] or something like that.
If your Post model has sub-models or collections, your model parse() will then take the sub-object property name and do something with it.
// In Post Model definition
parse:function(response) {
if (response.sub) {
// create some model or collection etc.
}
}
You might have to pass an option parse:true when you do your collection fetch.
I posted something along these lines which might help you see how sub-models can be instantiated on fetch calls.
Backbone.js: Load multiple collections with one request
Cast/initialize submodels of a Backbone Model
I hope this helps.