I am using Backbone with a RESTful interface that is provided by a 3rd party (so I can not change it).
For one of the models (which is read only) the RESTful interface states that you use the HTTP OPTIONS verb not the GET verb. Can I tell backbone to use "OPTIONS" not "GET" just for that model as other models use GET
You should be able to do something like this in your model definition:
sync: function(method, model, options){
if(method !== "read") throw "Read-only model cannot be called with non-read method";
options || (options = {});
var params = {type: "OPTIONS", dataType: 'json'};
if (!options.url) {
params.url = _.result(model, 'url') || urlError();
}
var xhr = options.xhr = Backbone.ajax(_.extend(, options));
model.trigger('request', model, xhr, options);
return xhr;
}
In opther words, you need to rewrite the sync method for your model so it behaves like you want it to (i.e. sending OPTIONS verb). Take a look at Backbone's sync implementation for inspiration: http://backbonejs.org/docs/backbone.html#section-143
Related
I want to configure an angular $resource. This $resource should allows to call distant web services.
// Gets all the nested-resources of a parent-resource
1) GET /parent-resource/nested-resources
// Gets a nested-resource by id
2) GET /nested-resource/{id}
// Create a nested resource
3) POST /nested-resource
In my scenario, i want retrieve all the nested resources using the first web service. When a nested resource is modified by the user, the nested resource will be save using the third web service.
var NestedResource = $resource('/nested-resource/:id');
Publishable.prototype.isNew = function() {
return this.id === undefined;
};
... lot of functions...
This configuration works well to hit the 2nd and 3rd web services but how can i configure the resource to use the 1st. Several functions are register on the nested resource prototype, i would like to avoid the creation of a specific $resource.
Publishable.prototype.getByParentId = function(parentId) {
// ??
}
Create a custom action on the resource as follows:
var NestedResource = $resource('/nested-resource/:id',,
{ action: "getAll",
method: "GET",
isArray: true,
url: '/parent-resource/:parentId/nested-resources'
});
Now you should be able to do:
var allItems = NestedResource.getAll({parentId: 1});
to retrieve the list for parentId 1.
The restful api im calling wraps the desired payload (of models) within 'Payload'
I can ONLY get it to work this way, by 1. adding each into this.add(model) and 2. returning the array
Note: 'this' is a Backbone Collection
parse: function(resp, xhr) {
var that = this;
var ourPayload = resp.Payload;
ourPayload.forEach( function(model) { that.add(model);} );
return ourPayload;
},
In all examples I'd expect to simply do
parse: function(resp, xhr) {
return resp.Payload;
}
The format of the rest api is "{"Success":"true", ...., "Payload":[{model},{model},{model}], .... }
Can one explain the need to the collection.add(model)
The problem is that during a Collection fetch each Model's parse will also be called if they are defined. And likely the model's parse function will be defined in just this case: the RESTful API format is using a wrapper around the payload.
Backbone docs
After fetching a model or a collection, all defined parse functions
will now be run. So fetching a collection and getting back new models
could cause both the collection to parse the list, and then each model
to be parsed in turn, if you have both functions defined.
Therefore you need to define a parse as below. Note that 'Payload' is the name of your wrapper, be it payload, data, results,,
// Within a collection, it will not.
parse: function (response, xhr) {
if (_.isObject(response.Payload)) {
return response.Payload;
} else {
return response;
}
}
Now that clue came from nikoshr in this SO answer
Further in some cases, when your entrepid backend guy has wrapped a Model in an array then you gotta do the following with the Payload index [0]
parse: function (response, xhr) {
if (_.isObject(response.Payload)) {
return response.Payload[0];
} else {
return response;
}
}
This should work (it should be put into the collection):
parse: function (response) {
return response.Payload;
}
See the documentation. The Twitter Search API the doc refers to is documented here. The response you get has basically the same structure: you get an object, with a field containing the array of models. The same technique works well when one calls an ASMX webservice, see the example here.
I'm doing an application with Backbone.js and Require.js. I have an "Opportunities" Backbone collection and I needed to modify the fetch method because the data from the server comes inside a "results" object.
I did the trick by doing something I found in this question, at this point all looks good.
The problem is that I noticed that the fetch method is asking the server for the data TWO TIMES and not ONE as expected.
I am testing and now I found that if I remove this code: return Backbone.Collection.prototype.fetch.call(this, options); Backbone asks the url for the data only one time, obviously this code is causing the problem but I don't know the reason.
This is my Backbone collection
define([
'backbone',
'models/Opportunity'
], function(Backbone, Opportunity){
var Opportunities = Backbone.Collection.extend({
url: "/api/v1/opps/",
model: Opportunity,
// Need to have custom fetch because json data is coming inside a
// "results" array inside the JSON.
fetch : function(options) {
// store reference for this collection
var collection = this;
$.ajax({
type : 'GET',
url : this.url,
dataType : 'json',
success : function(data) {
// set collection main data
collection.reset(data.results);
}
});
// For some reason this is causing a double request
// to the server
return Backbone.Collection.prototype.fetch.call(this, options);
}
});
return Opportunities;
});
Someone knows the reason because this error is happening?
It's fetching it twice because you're using jQuery to fetch it directly, the calling the models own fetch method which will call an AJAX request as well.
If you want to return data.results back to your collection (and subsequently models), you can use the parse method, like:
var Opportunities = Backbone.Collection.extend({
url: "/api/v1/opps/",
model: Opportunity,
parse: function(data){
return data.results;
}
});
Hello i have just dived into backbone.
What i am trying to do is make a collection of low_resolution photos from the instagram api feed.
I have model for user that stores all the instagram info like access_token and,
App.Models.Ig_photo({});
And a collection ,
App.Collections.Ig_photos({function() {
model: App.Models.Ig_photo,
url: "https://api.instagram.com/v1/users/self/feed?access_token=",
sync:function (method, model, options) {
options.timeout = 10000; // required, or the application won't pick up on 404 responses
options.dataType = "jsonp";
return Backbone.sync(method, model, options);
},
parse: function( response ) {
return response.data
}
}});
Now i have some issues here, that my collection doesn't get populated when i do fetch and secondly what i wanted was the accesstoken is saved in my another model called user so how do i access it here?
Also it would be great if someone suggests the approach im taking is correct or not. ?
You could add the API key to the collection model:
App.Collections.Ig_photos.access_token = OtherModel.get("access_token");
And use a function for the collection URL:
url: function() {
return "https://api.instagram.com/v1/users/self/feed?access_token=" + this.access_token
},
I am building a small app that uses backbone.js on the client side, node.js/socket.io on the server side, and the connection goes trough websockets only.
Now how would I make my setup if I want to get the template and the data at once.
I do a fetch() in the router, which gets the data and the template. With this I construct my view -> collection -> model.
I do a fetch() in the view itself. (Maybe in the initialize)
===
I want to extend this problem with the following.
Let's say a user browses to http://mysite.com/products and the user is not logged in yet, he is not allowed to view the page. He has to be rerouted to /login.
The problem is I can only verify this on the server, so the server has to send back the correct data whether the user is logged in or not, and backbone.js has to deal with this.
Summarized:
I make a fetch to the server which will send back data + template html.
Backbone.js has to render the template with the data,
or reroute (Backbone.history.navigate('login', {trigger: true})) when the server sends back a flag.
You could use parse method in your Backbone collection for example :
Collections.Products = Backbone.Collection.extend({
url : '/products',
parse : function (response) {
//
// you should return JSON from your server and the object must be smth like
// { template : "<p>template for products</p>", data : productsInJSON }
//
if ( response.template && response.data ) {
this.trigger('template', response.template);
return response.data;
} else {
return response;
}
}
});
Views.Page = Backbone.View.extend({
initialize : function () {
_.bind(this, 'render');
var self = this;
this.collection = new Collections.Products();
this.collection.on('template', function(template) {
self.render(template);
});
},
render: function(template) {
$("div#page").html(template);
}
});
$(function() {
window.app = {};
window.app.view = new Views.Page();
// here you are sending {template:true} to '/products' in your server
window.app.view.collection.fetch( { data : { template : true } } );
});
If you are using socket.io, you should create a new Backbone.sync method for your requests.
It is a little bit out of the Backbone philosophy and the integrated GET, PUT, POST, DELETE methods, so there will be a lot of coding.
You could send a template for unlogged in users with no data for the collection.