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;
}
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.
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;
}
I might just be blind or something, but I really cannot figure out why I cannot access a sub object of a returned $Resource object that retrieved a bunch of JSON objects.
Resource
> $resolved: true
> $then: function (b, g) {var j=e(),h=
> data: Object
> 519bc5f6b2427a732be1c360: Object
The raw JSON looks like this:
{
"data": {
"519bc5f6b2427a732be1c360": {
"id": "519bc5f6b2427a732be1c360",
"planning": {
"id": "519bc5f6b2427a732be1c355"
}
}
}
}
Can anyone explain me why this doesn't work:
var training = Training.query()
console.log(training); // returns the entire $Resource
console.log(training.data); // returns: undefined
Here is the explanation - from the Angular docs:
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most case one never has to write a callback function for the action methods.
So this works:
var training = Training.query(function(value){
// this is the callback function
console.log(training === value); // true - it's the same object
console.log(training.data); // and now it has data
});
Try getting the data like this:
var training = Training.query(function($val) {
console.log($val);
console.log($val.data);
});
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
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.