I have a simple model called BaseModel that extends from a Backbone.Model. According to Backbone.js documentation I've overridden parse method to work with a preexisting API.
The parse method is called in two different situation. When from a collection I want to fetch to grab all data. When from a model I want to fetch a specific data.
Within BaseModel to differentiate the behavior I'm doing the following.
parse : function(response, options) {
var result;
if(options.collection) {
// I'm coming from the fetch that belongs to the collection
// manipulate result here...
} else {
// I'm coming from the fetch that belongs to the model
// manipulate result here...
}
return result;
}
Is this appraoch valid or is there a better way to achieve this?
Edit 1
I thought on Andrew answer but the situation I need to manage is weird. In fact, when the parse method is called the first time (from the collection) data are parsed and properties for the model are created. Then, when the parse method is called from the model itself, additional data are parsed and properties for the model are merged to the first ones.
Edit 2
In my situation, for example, response coming from collection contains an array of objects where each object has a property a. Conversion can be applied, e.g. date obj. Then, response coming from model contains b. Also here conversion can be applied. At the end both properties will be merged into the same model but they are coming from different fetch calls.
Notice that response in collection is already an array. So, I do not to differentiate or split nothing here. I would just know that if I come from collection, I will find a, b otherwise.
Read the fetch in the collection as give me all models, while the other call as based on a model returned from the collection, enrich it by details.
Backbone.Collection also has a parse method. In my opinion the correct way is to implement it for both your BaseModel and your Collection.
The collections parse method only needs convert the data to be an array of unparsed models. It then delegates to the BaseModel parse method automatically to parse each one individually.
e.g.,
BaseModel {
parse : function(response, options) {
var result;
// I'm coming from the fetch that belongs to the model
// manipulate result here...
return result;
}
}
BaseCollection {
parse : function(response, options){
// I'm coming from the fetch that belongs to the collection
// Turn it into an array.
return response.split('mydelim');
}
}
From your edit 2, It looks like your approach is the right idea. I would however say that if I where to do it, I would test the returned object for properties rather than the context of the call so I don't need to care about the datasource,
parse : function(response, options) {
var result = {};
if(response.a){
result.c = response.a;
} else if(response.b){
result.c = response.b;
}
...
return result;
}
Related
I want to store an object as a string, and then convert ot back to an object and call a method of this object.
user.delete() // this works
self.user = JSON.stringify(user)
const storeUser = JSON.parse(self.user)
storeUser.delete() // Error: delete is not a function
If JSON.parse and JSON.stringify would allow a copy of methods, your code would be insecure and would have risks of people running arbitrary code on your server/computer since normally JSON string comes from external sources.
You should allow your User's class to parse a json and create a new instance based on that json:
class User {
constructor(someRandomProperty) {
// ... code
}
static fromJson(json) {
let parsedJson;
try {
parsedJson = JSON.parse(json);
} catch (error) {
throw new Error("Invalid Json User");
}
return new User(parsedJson.someRandomProperty);
}
delete() {
// .... code
}
}
And then you would copy the user like this:
User.fromJson(yourJsonStrinHere)
As has been said in comments, functions/methods are not part of the JSON format so they are not present when you serialize to JSON. Same with class names and such. So, when you call JSON.stringify(), it does not include any information about what type of class it is or any of the methods associated with that object. When you then call JSON.parse(), all you will get back is instance data in a plain object. That's just one of the limitations of JSON.
Normally, you will not serialize code like methods. Instead, you will serialize what type of object it is, serialize the relevant instance data and then you want to reinstantiate the object, you will look at the data to see what type of object it is, call the constructor to make an object of that type and pass to the constructor the data you serialized from the prior object and you will have built a version of the constructor that takes exactly the data you are serializing so it can properly initialize the new object.
Hi , I have some issue with parse method!!!!!
as you can see in backbone js document the parse method in collection has this syntax :
collection.parse(response, options)
1) I want to know why we should use / override the parse method and what is the main usage of that?
2) I read some article and I get that the parse method give us the data structure for the client-side.
3 ) I really have issue for understanding the arguments of parse method .
- what is the options for??
can you give me an example of using parse method with two parameters?
Thanks!
The docs have a nice summary:
parse is called by Backbone whenever a collection's models are returned by the server, in fetch. The function is passed the raw response object, and should return the array of model attributes to be added to the collection. The default implementation is a no-op, simply passing through the JSON response.
http://backbonejs.org/#Collection-parse
1) You should return an array of model attributes. If your JSON response only has this then you don't need to do anything. Typically the parse override is used simply to point inside the JSON object at the right part. For example if your response was like this:
{
httpCode: 200,
responseMessage: 'success',
data: [ {model1}, {model2} ...]
}
Then you would need to override parse to point to the data key:
parse: function(response) {
return response.data;
}
2) They meant that the response arg is the object which was returned by the server.
3) The second options arg is the options that was passed to the .fetch call. You don't need to worry about it unless you want to do some specific logic based on the URL, the HTTP method or anything else that can be passed to fetch (and jQuery.ajax options and some Backbone ones like reset).
4)
parse: function(response, options) {
// For some reason POST requests return a different data structure.
if (options.method === 'POST') {
return response.data;
}
return response;
}
I am trying to fetch a series of objects from my Node.js-based, MongoDB instance using Restangular; however, I am not able to guarantee the number of objects I wish to grab by ID will always be the same.
To demonstrate, here is a code snippet of the principle:
Restangular
.several('users',
userList[0], userList[1], userList[2], userList[3], userList[4],
userList[5], userList[6], userList[7], userList[8], userList[9])
.get().then(function (users) { //...
userList is an array of IDs passed in as a part of a method:
requestUsersById = function (userList) { //...
The problem is that I cannot guarantee the size of the array. Is there a way to pass an array of IDs using Restangular? Or, am I just stuck making separate requests for each?
The ideal result would be something like:
Restangular
.several('users', userList)
.get().then(function (users) { //...
The Restangular API doesn't seem to natively support this, but I believe you can accomplish what you're trying to do by making using the apply() method.
In this case, you'd append the name of the collection users to the head of your userList array. Try this out:
// Store the userList in a new array to preserve the initial list
// (not sure if you use it somewhere else
var usersQuery = userList;
// Shift the collection name to be the first parameter in the array
usersQuery.unshift("users");
// Perform the Restangular call
Restangular
.several.apply(null,usersQuery)
.get().then(function (users) { //...
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 read and read the docs on these two methods, but for the life of me cannot work out why you might use one over the other?
Could someone just give me a basic code situation where one would be application and the other wouldn't.
reset sets the collection with an array of models that you specify:
collection.reset( [ { name: "model1" }, { name: "model2" } ] );
fetch retrieves the collection data from the server, using the URL you've specified for the collection.
collection.fetch( { url: someUrl, success: function(collection) {
// collection has values from someUrl
} } );
Here's a Fiddle illustrating the difference.
We're assuming here that you've read the documentation, else it'l be a little confusing here.
If you look at documentation of fetch and reset, what it says is, suppose you have specified the url property of the collection - which might be pointing to some server code, and should return a json array of models, and you want the collection to be filled with the models being returned, you will use fetch.
For example you have the following json being returned from the server on the collection url:
[{
id : 1,
name : "a"
}, {
id : 2,
name : "b"
}, {
id : 3,
name : "c"
}]
Which will create 3 models in your collection after successful fetch. If you hunt for the code of collection fetch here you will see that fetch will get the response and internally will call either reset or add based on options specified.
So, coming back to discussion, reset assumes that we already have json of models, which we want to be stored in collection, we will pass it as a parameter to it. In your life, ever if you want to update the collection and you already have the models on client side, then you don't need to use fetch, reset will do your job.
Hence, if you want to the same json to be filled in the collection with the help of reset you can do something like this:
var _self = this;
$.getJSON("url", function(response) {
_self.reset(response); // assuming response returns the same json as above
});
Well, this is not a practice to be followed, for this scenario fetch is better, its just used for example.
Another example of reset is on the documentation page.
Hope it gives a little bit of idea and makes your life better :)
reset() is used for replacing collection with new array. For example:
#collection.reset(#full_collection.models)
would load #full_collections models, however
#collection.reset()
would return empty collection.
And fetch() function returns default collection of model