How to convert json array to backbone collection - backbone.js

This is the json data returned by a rest-full service as a response:
{
"calendarEvent":[
{
"event_date":"2014-09-01",
"event_location":"bbbbb",
"event_name":"aaaa"
},
{
"event_date":"2014-08-04",
"event_location":"hotel",
"event_name":"bday"
},
{
"event_date":"2014-09-11",
"event_location":"cccc",
"event_name":"aaaa"
}
]
}
How can I convert this json array to backbone collection?

Override collection.parse when you want to customize how Backbone handles a server response :
parse collection.parse(response, options)
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.
In your case, your collection would look like:
var C = Backbone.Collection.extend({
parse: function(response) {
return response.calendarEvent;
}
});

Related

Sending json object as json array to an API

I have an API that accepts data format as [ { "record_id": "TestID3" } ]. I am trying to send record_id field using the form below in my angular project:
html:
<input id="record_id" type="text" class="form-control" [(ngModel)]="member.record_id" name="record_id" #record_id="ngModel" placeholder="Enter Record ID">
component.ts:
export class MembersAddComponent implements OnInit {
member: Array<Object> = [];
constructor(private service: DataService ) { }
ngOnInit() {
}
submit() {
this.service.importRecord(this.member).subscribe(member => {
this.member = member;
}, error => {
console.log(error);
});
}
}
And my service.ts:
importRecord(data): Observable<any> {
const formData = new FormData();
formData.append('token', this.token);
formData.append('content', this.content);
formData.append('format', this.format);
formData.append('returnFormat', this.returnFormat);
formData.append('type', this.type);
formData.append('overwriteBehavior', this.overwriteBehavior);
formData.append('forceAutoNumber', this.forceAutoNumber);
formData.append('data', data);
formData.append('returnContent', this.returnContent);
return this.http.post(this.baseUrl, formData).map(res => res.json())
.catch(this.handleError);
}
The error that I get is below:
{"error":"The data being imported is not formatted correctly. The JSON must be in an array, as in [{ ... }]."}
I also tried member:any = {}, member:Object = {}; but I got the same error. I am thinking that I am unable to format my member object as requested format. But I couldn't make it as desired format.
[ { "record_id": "TestID3" } ]
That is an array, containing a single element, which is an object.
member: Array<Object> = [];
that defines an array with no element at all.
[(ngModel)]="member.record_id"
That will try to read and write the property record_id of member, which is an array. It will not magically add an element to the array and set its property.
So what you need is an object that will be populated by the form. And then you need to put that object into an array before to send the array to the API.
Start by defining an interface to describe your object:
interface Member {
record_id: string;
}
Then use one as the model for your form:
member: Member = {
record_id: '';
};
...
[(ngModel)]="member.record_id"
Then put that member into an array before sending it:
submit() {
const data: Array<Member> = [this.member];
this.service.importRecord(data)...
It's difficult to tell if this is due to an incorrectly formatted response from the POST or the body of the POST.
Things you can do:
Check the network tab in Chrome to verify that the request is being sent, it's content is valid JSON (use an online validator)
Check your API backend to see if the data you're sending is being saved, if so the error is with the format of the JSON in your response.
Verify in Chrome that the response data in the network request is valid JSON.
If all of these are true, you may need to consider using headers such as {requestType: 'json'} as detailed in the Angular docs here: Request/Response Headers
If these are not true, then you will need to change the model of the object you are sending to reflect the object which is expected.

Backbonejs - Avoid parse after save

Backbone documentation says,
parse is called whenever a model's data is returned by the server, in
fetch, and save. The function is passed the raw response object, and
should return the attributes hash to be set on the model.
But i have customized parse function for my model. I want to execute it only when i fetch data not when i save data.
Is there a way to do it? I can check my response inside parse function. But is there any built-in option to do it?
This is from the backbone source file regarding saving a model:
var model = this;
var success = options.success;
options.success = function(resp) {
model.attributes = attributes;
var serverAttrs = model.parse(resp, options);
if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
return false;
}
if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
};
You could pass a custom option on your save like: model.save(null, { saved: true }), then in your custom parse:
parse: function(response, options) {
if ( options.saved ) return this.attributes;
// do what you're already doing
}
I haven't tested this at all, but it should at least get you started.
Just pass a parse:false into the save method as an option.
m = new MyModel()
s.save(null, {parse: false})

Backbone.js parse for saving

I have a model which keeps some other models in an attribute array. When these models are stored, however, I don't want to keep the sub-modules around--instead, I want to store the primary keys, and then when the model is fetched from the server, its parse will "reconstitute" them by fetching the related models.
What is the best approach to accomplishing this? The closest I've come to getting it to work is overriding the sync method:
sync : function(method, model, options) {
var topics = this.get('topics');
model.attributes.topics = _.pluck(topics, 'id');
var ret = Backbone.Model.prototype.sync.call(this, method, model, options);
this.attributes.topics = topics;
return ret;
},
but this regularly fails, leaving the keys in the attributes instead of the full models & consequently crashing.
Parse function (slightly paraphrased):
parse : function(response) {
response.topics = _.map(response.topics, function(item) {
return app.topics.getByPK(item);
}
return response;
}
What I would do would be something more along these lines:
parse : function(response) {
this.topics = _.map(response.topics, function(item) {
return app.topics.getByPK(item);
}
return response;
}
Which keeps your array of ids intact at all times, and you have access by using this.topics instead of this.get('topics') or this.attributes.topics

JSON.stringify() with collection in backbone.js

Can someone explain how JSON.stringify() magically ONLY stringifies JSON as fetched by URL and does NOT bother with other backbone-specific parts of the full collection object?
I am curious about underlying implementation and/or design patterns that explain this very impressive capability. I had to use json2.js to get stringify functionality, so I don't think backbone is overriding or decorating stringify.
I discovered that if I pass a collection directly to JS OBJECT code, the code "sees" model keys and other backbone-specific parts of a collection object, whereas if I perform JSON.stringify THEN jquery.parseJSON on that stringified object, my code "sees" only the JSON as returned by URL.
Code:
enter code here
$(function () {
var Person = Backbone.Model.extend({
initialize: function () {
// alert("Model Init");
}
}),
PersonList = Backbone.Collection.extend({
model: Person,
url: '/Tfount_Email/Email/SOAInbox',
initialize: function () {
// alert("Collections Init");
}
}),
personlist = new PersonList();
personlist.fetch({
error: function () {
alert("Error fetching data");
},
success: function () {
// alert("no error");
}
}).complete(function () {
// first call to makeTable w collection obj, we see MORE than just the JSON returned by URL
makeTable(personlist);
// stringify then parse, we see only JSON returned by URL
jsonString = JSON.stringify(personlist);
var plistJSON = jQuery.parseJSON(jsonString);
makeTable(plistJSON);
});
});
function makeTable(obj) {
var type = typeof obj
if (type == "object") {
for (var key in obj) {
alert("key: " + key)
makeTable(obj[key])
}
} else {
alert(obj)
}
}
This is the intended and by-design behavior of JSON.Stringify. From Douglas Crockford's JSON2.js file:
When an object value is found, if the object contains a toJSON method, its toJSON method will be called and the result will be stringified.
https://github.com/douglascrockford/JSON-js/blob/master/json2.js#L38-39
When you call JSON.stringify on a Backbone.Collection, it calls that collection's toJSON method, as described by this comment.

Setting parameters on collection methods using Backbone.Rpc

Using the Backbone.Rpc plugin [ https://github.com/asciidisco/Backbone.rpc ] I am attempting to send parameters on the read method when fetching a collection. When working with a single model instance you can add parameters to a method call by setting the value of a model attribute.
var deviceModel = Backbone.model.extend({
url: 'path/to/rpc/handler',
rpc: new Backbone.Rpc(),
methods: {
read: ['getModelData', 'id']
}
});
deviceModel.set({id: 14});
deviceModel.fetch(); // Calls 'read'
// Request created by the 'read' call
{"jsonrpc":"2.0","method":"getModelData","id":"1331724849298","params":["14"]};
There is no corresponding way that I am aware of, to do a similar thing prior to fetching a collection as there is no 'set' method available to backbone collections.
var deviceCollection = Backbone.collection.extend({
model: deviceModel,
url: 'path/to/rpc/handler',
rpc: new Backbone.Rpc(),
methods: {
read: ['getDevices', 'deviceTypeId']
}
});
// This is not allowed, possible work arounds?
deviceCollection.set('deviceTypeId', 2);
deviceCollection.fetch();
// Request created by the 'read' call
{"jsonrpc":"2.0","method":"getDevices","id":"1331724849298","params":["2"]};
Is it possible to pass parameters to collection methods using Backbone.Rpc? Or do I need to pass collection filters in the data object of the fetch method?
I updated Backbone.Rpc (v 0.1.2) & now you can use the following syntax to add "dynamic"
arguments to your calls.
var Devices = Backbone.Collection.extend({
url: 'path/to/my/rpc/handler',
namespace: 'MeNotJava',
rpc: new Backbone.Rpc(),
model: Device,
arg1: 'hello',
arg2: function () { return 'world' },
methods: {
read : ['getDevices', 'arg1', 'arg2', 'arg3']
}
});
var devices = new Devices();
devices.fetch();
This call results in the following RPC request:
{"jsonrpc":"2.0","method":"MeNotJava/getDevices","id":"1331724850010","params":["hello", "world", "arg3"]}
Ah,
okay, this is not included at the moment, but i can understand the issue here.
I should be able to add a workaround for collections which allows the RPC plugin to read
collection properties.
var deviceCollection = Backbone.collection.extend({
model: deviceModel,
url: 'path/to/rpc/handler',
rpc: new Backbone.Rpc(),
deviceTypeId: 2,
methods: {
read: ['getDevices', 'deviceTypeId']
}
});
Which will then create this response:
{"jsonrpc":"2.0","method":"getDevices","id":"1331724849298","params":["2"]};
I will take a look this evening.

Resources