I have a large json server response which nests models in models. I have no way of making the server rest based. I need to parse the json tree into collections and collections within collections. I would then like to export the backbone models in the same json structure to the server after modification.
i have a collection called sections, and using this method to nest the questions collection within each section model:
http://documentcloud.github.com/backbone/#FAQ-nested
the top level of the json response is the array of sections so i have been able to pass that directly to the sections collection, I have then used the initialize method to parse out the child questions and then deleted them from the attributes. But this means i dont get the model.questions returned in any toJSON on the sections collection.
SCC.Models.Section = Backbone.Model.extend({
initialize: function() {
var questions = this.get('questions');
this.questions = new SCC.Collections.Questions(questions);
delete this.attributes.questions;
}
});
SCC.Collections.Sections = Backbone.Collection.extend({
model: SCC.Models.Section
});
SCC.Sections = new SCC.Collections.Sections();
//imagine window.SectionData is the server response
SCC.Sections.reset(window.SectionData);
Hopefully I am clear. Let me if you need more info.
thanks.
If you have a limited number of nested models which are under you control and are not subject to frequent change, the easy way is to override toJSON() method for your models that have nested collections:
SCC.Models.Section = Backbone.Model.extend({
initialize: function() {
var questions = this.get('questions');
this.questions = new SCC.Collections.Questions(questions);
delete this.attributes.questions;
},
toJSON: function() {
var json = Backbone.Model.prototype.toJSON.call(this);
json.questions = this.questions.toJSON()
return json
}
});
However, if you have a lot of nested collections which are subject to change, more abstract way would be nice.
I can think for example of storing all nested collections in model's special property (say nestedCollections) and then monkey patching Backbone.Model.prototype.toJSON so that it will always parse all nestedCollections and add them to JSON object before returning it.
Another way would be to use Backbone relational instead of nested models (it automatically handles toJSON so you won't have to think about it).
Related
I have a backbone application and a RESTful api. I used the sample created by Coenraets to understand the architecture of a backbone app, but I decided to setup my own structure and just use the data for testing.
I want to know the best way to return data from the RESTful api. I currently have my app folder structure setup with model, collection, view and service folders. I have a node server running with express that handles the backend and is working fine.
What I want to know is what is the best practice for accessing the restful data api? Should I do that in my service class or in my view class? How do I make this work dynamically using the returned data from my restful api: http://localhost:3000/employees
It seems like there are many ways to do this and for now I just want something that works, but eventually I do want to know what is the best way to do it. Ultimately I want to have a CRUD setup. But I'm not sure where that should be setup. Similar to what is detailed here: http://www.codeproject.com/Articles/797899/BackBone-Tutorial-Part-CRUD-Operations-on-Backbone
My files are as follows:
employeecolletion.js
var Backbone = require('backbone');
var Employee = require('../models/employeemodel.js');
module.exports = Backbone.Collection.extend({
model: Employee,
url:"http://localhost:3000/employees"
});
employeemodel.js
var Backbone = require('backbone');
var EmployeeCollection = require('../collections/employeecollection.js');
module.exports = Backbone.Model.extend({
urlRoot:"http://localhost:3000/employees"
// initialize:function () {
// this.reports = new EmployeeCollection();
// //this.reports.url = this.urlRoot + "/" + 1 + "/reports";
// }
});
employee.js (employee view that binds to my template)
var fs = require('fs');
var base = require('./base.js');
var EmployeeList = require('../collections/employeecollection.js');
var employeeService = require('../services/employeeService.js');
var template = fs.readFileSync('app/templates/employee.mu', { encoding: 'utf8' });
module.exports = base.extend({
el: '.view',
template:template,
collection: employeeService.collection,
initialize: function () {
this.viewModel = {
employee_list: this.collection.toJSON()
//employee_list: this.collection.fetch() --HERE I EXPERIMENTED WITH FETCHING THE DATA
};
this.render();
}
});
employeeservice.js (file in service folder that would ideally return the collection which I would just bind to my template in they employees view file)
var EmployeeCollection = require('../collections/employeecollection.js');
//if wanting to pass in data manually
var employee_list = [
{
id:1,
firstName:"James",
lastName:"King",
fullName:"James King",
managerId:0,
managerName:"",
title:"President and CEO",
department:"Corporate",
cellPhone:"617-000-0001",
officePhone:"781-000-0001",
email:"jking#fakemail.com",
city:"Boston, MA",
pic:"james_king.jpg",
twitterId:"#fakejking",
blog:"http://coenraets.org"
}
];
//HERE I WAS EXPERIMENTING WITH A DIFFERENT SYNTAX TO DO THE FILTERING BY ID
//IN MY SERVICE AND SIMPLY RETURNING THE FINAL DATA I WANT TO MY VIEW CLASS
// var employees = new EmployeeCollection({id: id});
// employees.fetch({
// success: function (data) {
// console.log(data);
// }
// });
module.exports = {
collection: new EmployeeCollection(employee_list)
};
Backbone is meant for RESTful services.
I'll try to explain the basics using some easy to understand terms.
So backbone is based on models and views.
The model is responsible to the data.
That means, that the model is the one who fetches the data from the server and stores it.
In an interactive application, the model should have a url or urlRoot properties which indicate what is the url of the specific resource this model refers to.
For example, if we had a Person resource, and assuming we are consuming a standard RESTfult service, I would expect something similiar to this:
var Person = Backbone.Model.extend({
url : 'http://localhost:3000/api/Person'
});
That actually lets us create new instances of this model and manipulate it.
This url will be used by the model for all CRUD operations related to it.
For example, if we now create a new instance:
var person = new Person();
We now have the following basic CRUD operations:
fetch: this method is executing an async AJAX GET request behind the scenes, and injects the data into the model.
Now, after we fetched the data, we can use it by simply calling get:
person.get('name'); * assuming there's a name property.
save this method is exectuing an async AJAX POST or PUT request behind the scene.
If the model's idAttribute is undefined, it will executed POST, otherwise PUT. The idAttribute is a model property which indicates what is the model's unique id.
A sample usage:
person.set({name : 'Mor'});
person.save();
The abvoe will execute a post request with the name: 'Mor' in the request body.
If for example I fetched the model, and already have an idAttribute assigned, calling the same save method will use the PUT request.
destroy this method will execute a DELETE request behind the scene.
Sample usage: person.destroy();.
Obviously I have just shown you the basic usages, there's a lot more options out there.
A collection is simply a list of models so there's not much to explain, you can read more here: http://backbonejs.org/#Collection
A view is all you see. It is the visual part of the application.
What Backbone lets us do, is to bind views to models and collections.
By that, we can create some dynamic content and visuals.
A basic view would like something like that:
var PersonView = Backbone.View.extend({
el: '.person',
initialize: function(){
this.listenTo(this.model, "change", this.render);
},
render: function(){
this.$el.html("hello :"+this.model.get("name"));
}
});
As you can see, I used listenTo. It is an event listener that calls render each time the model changes.
When I refer to this.model I refer to a model I will pass to the view when I initiate it:
var view = new View({ model : person});
By that, and since I used listenTo, my view is now binded with the person model.
This is basically it.
Obviously, there's a lot more to learn and understand, but this pretty much covers the basics.
Please refer to http://backbonejs.org/ and read some more information.
I am very new in backbone js.
I am trying to filter some specific key and values in backbone js model extend here is the code below.
var items = ["open","close"];
var ReportModel = Backbone.Model.extend({
url: function() {
return tab+".json";
}
});
where tabe is dynamic json file name.In my json file many key value pair are there but I want to load only those key which is mentioned in items list.
I saw some where using parse function but that a;so did not work out.Please do let me know how to filter the specific keys form json using the backbone.
I also tried creating a dict from json and pass it to model like.
var ReportModel = Backbone.Model.extend({
"open":{.......}
});
but there I am getting issue.
throw new Error('A "url" property or function must be specified');
Please help me out with this.
You are missing some steps to be succesfull on your task.
First a note about the error: Backbone expects a string on the url property while you're passing a function. If you want to use a function to return your url dinamically use urlRoot.
Now onto the real coding:
since you talk about a json file that has multiple key value, maybe you should declare your model as a key-value object, and then create a Backbone.Collection that will wrap your models.
A Backbone.Collection expose a lot of utilities that can help us modeling the results, in this case by using the where() function of our collection you will be able to filter the data after you have retrieved from the remote file.
Alternatively to filter your collection if you need more control over the function you can always call the undescore function filter() .
Please refer to the official documentation of underscore and backbone, as you will find a lot of functions that can help you and most of them have an example that shows how to use them.
Now that we have everything lets create our Backbone.Collection that will wrap your already defined model:
var ReportCollection = Backbone.Collection.extend({
model: ReportModel,
urlRoot: function(){
return 'yoururl.json';
}
});
now if you want to filter the result you can simply fetch the collection and perform a filter on it:
var myReports = new ReportCollection();
//call the fetch method to retrieve the information from remote
myReports.fetch({success: function(){
//the collection has been fetched correctly, call the native where function with the key to be used as a filter.
var filteredElements = myReports.where({my_filter_key : my_filter_value});
});
in your filteredElements you will have an array of object made up of all the model that matched the key/value passed to the where function.
If you need a new Collection from that you just need to pass the result as argument: var filteredCollection = new ReportCollection(filteredElements);
You can use _.pick() in the parse method as shown below:
var items = ["open", "close"];
var ReportModel = Backbone.Model.extend({
url: function() {
return tab + ".json";
},
parse: function(response) {
return _.pick(response, items);
}
});
I have a model which I need to save to JSON and re-load as required. The idea is that the user can save the model to a basket or database (as a JSON string) and then reload it. It's simple enough to create the JSON and save it but re-loading is seemingly much more difficult. The problem is that my model includes some complex objects similar to the Knockout Contacts example:
http://knockoutjs.com/examples/contactsEditor.html
I can even generate the JSON and pass it via a web service into an .NET object.
I've looked at the Knockout documentation for loading a data model:
http://knockoutjs.com/documentation/json-data.html
...and I can easily restore my "simple" observables but I don't see how I can initialise my arrays of complex objects where these objects also contain observable arrays. Any ideas?
I've even tried using the mapping plugin but the resulting JSON doesn't contain the information in the arrays.
Doing this creates the JSON and all my data is in there:
var jsonData = JSON.stringify(ko.toJS(self.contacts), null, 2)
Ideally, I just need to reverse this or at least create all the objects one by one.
Thanks for looking :)
The mapping plugin should work. I'm not sure if i understood correctly your situation, but try somethig like that:
ViewModel = function() {
this.contacts = ko.observableArray();
}
var model = new ViewModel();
ko.applyBindings(model);
var json = '{"contacts":[{"name":"Tom","phones":["00000000","1111111111"]},{"name":"Bill","phones":["222222222","333333333"]},{"name":"Bert","phones":["444444444","55555555"]}]}';
ko.mapping.fromJSON(json, {}, model);
The observableArray of "contacts" and "phones" in each contact are deserialized correctly from the json string.
http://jsfiddle.net/k4Sth/1/
Using Backbone.js I need to handle an array of strings returned from a JSON web service. Should I create a Backbone Collection to fetch this list? Here is the data I get from the web service:
["Question Designer","Adaptive Question Designer","Clickable image map","Essay","Fill in the blanks","Maple-graded","Matching","Mathematical formula","Multipart question","Multiple choice","Multiple selection","Numeric","Palette-based symbolic editor","True/false","Other"]
I've created this simple Collection to fetch the data:
var questionTypesCollection = Backbone.Collection.extend({
url: function() {
return apiBase + '/questions/types';
}
});
But when I try to fetch() the collection, I get this error:
Uncaught TypeError: Cannot use 'in' operator to search for 'id' in Question Designer
It looks like Backbone is trying to parse the strings as a model instead of seeing that it's just a raw string. How can I get the data into a Collection so I can use it in a View?
If you just need the strings, your best bet might be to just let jQuery (or Zepto--whatever has the $) handle the heavy lifting:
var names = [];
$.get(apiBase + '/questions/types', {}, function(result){
names = result;
})
After the fetch completes, the names variable will be populated with the results of your query.
This doesn't make a lot of sense, since backbone collection is designed to be, well, a collection of models.
However, you could override the parse method with your own parser.
I have a situation using backbone.js where I have a collection of models, and some additional information about the models. For example, imagine that I'm returning a list of amounts: they have a quantity associated with each model. Assume now that the unit for each of the amounts is always the same: say quarts. Then the json object I get back from my service might be something like:
{
dataPoints: [
{quantity: 5 },
{quantity: 10 },
...
],
unit : quarts
}
Now backbone collections have no real mechanism for natively associating this meta-data with the collection, but it was suggested to me in this question: Setting attributes on a collection - backbone js that I can extend the collection with a .meta(property, [value]) style function - which is a great solution. However, naturally it follows that we'd like to be able to cleanly retrieve this data from a json response like the one we have above.
Backbone.js gives us the parse(response) function, which allows us to specify where to extract the collection's list of models from in combination with the url attribute. There is no way that I'm aware of, however, to make a more intelligent function without overloading fetch() which would remove the partial functionality that is already available.
My question is this: is there a better option than overloading fetch() (and trying it to call it's superclass implementation) to achieve what I want to achieve?
Thanks
Personally, I would wrap the Collection inside another Model, and then override parse, like so:
var DataPointsCollection = Backbone.Collection.extend({ /* etc etc */ });
var CollectionContainer = Backbone.Model.extend({
defaults: {
dataPoints: new DataPointsCollection(),
unit: "quarts"
},
parse: function(obj) {
// update the inner collection
this.get("dataPoints").refresh(obj.dataPoints);
// this mightn't be necessary
delete obj.dataPoints;
return obj;
}
});
The Collection.refresh() call updates the model with new values. Passing in a custom meta value to the Collection as previously suggested might stop you from being able to bind to those meta values.
This meta data does not belong on the collection. It belongs in the name or some other descriptor of the code. Your code should declaratively know that the collection it has is only full of quartz elements. It already does since the url points to quartz elements.
var quartzCollection = new FooCollection();
quartzCollection.url = quartzurl;
quartzCollection.fetch();
If you really need to get this data why don't you just call
_.uniq(quartzCollecion.pluck("unit"))[0];