Backbone.js: PATCH url endpoint wrong - backbone.js

I have a model like this:
define([
'jquery',
'backbone'
], function ($) {
var MyModel = Backbone.Model.extend({
url: 'articles/',
initialize: function(){
}
});
return MyModel;
});
And this is the code for saving the object:
article = new Article();
status = t.currentTarget.textContent;
article.set('ready', {'status': status});
coords = this.model.get('location').coords;
article.set('geo', {'lat': coords.latitude, 'lng': coords.longitude});
article.save(null, { accessToken: true }).done(function(){
self.hideIcons();
});
But when I do the PATCH:
this.article.save({comment: comment}, {patch: true, accessToken: true});
The request is correctly PATCH, but the endpoint is wrong, the request is like this:
PATCH mydomain.com/articles/
As you can see, should be:
PATCH mydomain.com/articles/<last-model-id-created>/
Thanks.

Instead of setting the Model.url property, you should set Model.urlRoot. From the docs:
Specify a urlRoot if you're using a model outside of a collection, to enable the default url function to generate URLs based on the model id. "[urlRoot]/id"
If the model belongs to a collection, you can leave Model.urlRoot unspecified as well and set Collection.url instead.

Related

Backbone view listening to model, not firing on change or reset?

I am using a model to fetch location data from a service using POST.
However I am struggling to get the view to listen to that model when it finally receives a response.
Note I have overriden some model methods to fit the service I am using.
Model code
define([
'underscore',
'backbone',
], function (_, Backbone)
{
'use strict';
//model class declaration
var LocationsModel = Backbone.Model.extend({
locations: null, //this attribute stores the json response
//url for http post to get location data
url: '/testserver/getLocationData',
/**
#name constructor
#method initialise
**/
initialize: function ()
{
console.log("Location Model - Initialize");
this.fetchLocationData();
},
/**
#name fetchLocationData
#method fetchLocationData
#summary retrieves bulding/location data from service, overriden fetch function to use POST
**/
fetchLocationData: function ()
{
console.log("Location Model - Fetching Building/Location data from EAS");
this.fetch({
data: JSON.stringify({ "lastRefreshedDateTime": "2015-04-01T08:18:06.000+00:00", "uid": "" }), //refactor this - string literals which are static currently
type: "POST",
contentType: "application/json",
async : false, //this is bad but it is the only way I can get it to work
at the moment
reset: true //tried adding reset parameter but no luck
});
},
/**
#name parse
#method parse
#summary parse is a method inside backbone that can be overridden, in this override we set a model attribute to the JSOn response
**/
parse: function (response, xhr)
{
console.log("Location Model - Parsing response from EAS");
this.attributes = { "true": true }; //tried adding an attribute value to trigger "change:true"
this.locations = response;
//do other stuff
},
});
return LocationsModel;
});
In the view initialiser I have tried the following binds and listen to on the model however they don't seem to trigger after a response.
View code
model : new LocationModel(),
initialize: function ()
{
console.log("Filter View - Initialize");
this.render();
this.listenTo(this.model, 'change', this.render); //this only fires at the start when the model is initialised
this.model.on('change', this.render, this); //same as above
this.listenTo(this.model, 'reset', this.render); //not fired at all
},
For a collection it was fairly simple to listen to any changes that happened, however it seems to be a different ball game for Backbone models.
TLDR, how can I get the view to listen to a model successfull fetch request?
The sync event is the one you want to listenTo. It gets fired once a fetch has completed successfully. This line in the Backbone source is the culprit: Backbone source, line 578.
Your code should now look something like this:
View code
model : new LocationModel(),
initialize: function ()
{
console.log("Filter View - Initialize");
this.render();
this.listenTo(this.model, "sync", this.render);
},
Here's a fiddle which shows your code working. I used httpbin to mock the POST request. You can also remove the async: false parameter in the fetch request now :) (I've removed it in the fiddle example).

Loading Backbone model by custom attribute

Lets say, I have the following Backbone model :
var Meal = Backbone.Model.extend({
defaults: {
"appetizer": "caesar salad",
"entree": "ravioli",
"dessert": "cheesecake"
},
urlRoot : api/meals,
idAttribute : id,
// some other stuff
});
Assuming that I have a backend Spring MVC conroller that intercept GET requests, so when I fetch my model, using
myMeal.fetch();
my model gets loaded from the server.
for now everything is clear, my question is, what if I have another method on the backend that takes a string as parameter and return as responsebody, the right json element.
how can I make that call from my model ?
I'm looking for something like this :
var meal = new Meal({'entree': 'value'});
meal.fetch({
// if there is no id, and 'entree' is given, I want to call /
// api/findByEntree passing this.entree as parameter.
});
I want to make an Ajax call from the object itself to the backend, by specifying the url inside the Backbone model.
urlRoot can be a function so no need to override fetch. I believe you could just do something like this:
var Meal = Backbone.Model.extend({
defaults: {
"appetizer": "caesar salad",
"entree": "ravioli",
"dessert": "cheesecake"
},
urlRoot : function() {
return 'api/' + this.get('id') ? 'meals' : 'findByEntree';
},
idAttribute : id,
// some other stuff
});
You can override the default fetch, intercept the call, do some verification and then pass onto the original fetch:
var Meal = Backbone.Model.extend({
fetch: function(options) {
if(this.has('id')) {
Backbone.Model.prototype.fetch.call(this, options);
} else {
this.findByEntree(options);
}
},
fetchByEntree: function(options) {
...
}
});
however, keep in mind that you'll need some extra logic to deal with the case of trying to fetch a new Meal, which won't have neither id nor entree.

Custom URL for Create, Update and Delete in Backbone.js

I have requirement like backbone-js-model-different-url-for-create-and-update, but didn't get anything working out of it.
I have my Backbone Model as this:-
var task = backbone.Model.extend({
idAttribute: "TaskId",
defaults: {
TaskId: null,
Name: null,
TaskTypeId: null
},
// urlRoot: '/MyController/GetTasksAsync',
methodToURL: {
'read': '/MyController/Get',
'create': '/MyController/create',
'update': '/MyController/update',
'delete': '/MyController/remove'
},
sync: function (method, model, options) {
options = options || {};
options.url = model.methodToURL[method.toLowerCase()];
return Backbone.sync.apply(this, arguments);
}
});
Now, I am not getting how to call the Sync method?
This is what I tried but didn't work:-
this.model.sync("read",1,"");//(Get method, some hardcoded value /Get/1,no callback)
I similar lines how will I call the save method?
this.model.sync('update',model,'');
Am I missing anything??
I think what you're actually looking for are the Backbone Model methods fetch, save, and destroy which delegate to the sync method that you have overridden.
Here are the docs for fetch (save and destroy are nearby):
model.fetch
For example, to trigger the 'read' method you could call model.fetch().

BackboneJS model.url using collection.url

From my understanding the default behavior of Backbone JS Models are to return the Collection's URL, unless the model has a urlRoot specified. I can't seem to get the behavior to work.
From the documentation:
model.url() ... Generates URLs of the form: "[collection.url]/[id]" by default, but you may override by specifying an explicit urlRoot if the model's collection shouldn't be taken into account.
Here is my collection, and model respectively:
var MyCollection = Backbone.Collection.extend({
model: Model,
initialize: function(options){
this.options = options || {};
},
url: function(){
return "/theurl/" + this.options.param;
}
});
return MyCollection;
...
var MyModel = Backbone.Model.extend({
urlRoot: '/theurl',
initialize: function() {
}
});
return MyModel;
When a model is loaded without a collection, it works great and submits to /theurl, but when it's loaded into a collection, all methods submit to /theurl/param/.
If I'm understanding the documentation correctly, the urlRoot of the Model should override this behavior; and even then the models url should be /theurl/param/{MODEL-ID}.
Any ideas on what I'm missing/misunderstanding?
...
PS: The model: Model from the collection is brought in via RequireJS
It will always use the collection's url even if you have urlRoot specified.
The reason for urlRoot is so you can use it in an override, or if the model happens to not be in a collection ( for example maybe it gets removed, but still exists on the client).
So if you want to fetch or save the model and override the url generated by the collection you'll need to pass the urlRoot into these methods explicitly as an option. For example:
yourModel.save({ url: yourModel.urlRoot });
I agree the documentation is confusing and this caught me out recently too.
UrlRoot should be a function and model must have attributeId defined.
If you define your model like this all operation will be working if model is in collection or not.
Backbone add modelId at the end of URL that is returned by urlRoot function.
var MyModel = Backbone.Model.extend({
attributeId: 'myModelId'
urlRoot: function() {
return '/theurl';
},
initialize: function() {
}
defaults: {
myModelId: null
}
});
In the model, try using:
url: function() {
return 'your url goes here';
}

Backbone collection 0.9.9 - add event not work

Backbone collection 0.9.9 - add event not work
After update backbone to 0.9.9, i have a problem with (add).
(function () {
var Model = Backbone.Model.extend({
initialize: function () {
console.log('initialize Model');
}
});
var Collection = Backbone.Collection.extend({
model: Model,
url: "/json.json",
initialize: function () {
console.log('initialize Collection');
}
});
var View = Backbone.View.extend({
initialize: function () {
console.log('initialize View');
}
});
var collection = new Collection([
{
"id" : "001",
"name" : "Дарья",
"text" : "1 Вопрос - Ответ 1"
}
]);
collection.on('add', function () {
console.log('edd event', this)
});
collection.fetch({
add: true,
//silent: false,
success: function (model) {
console.log('fetch')
}
});
}());
console.log('edd event', this) - not work (
in old versions it works
It would appear that the add option to collection.fetch is no longer supported.
From 0.9.2 source (collection.fetch):
collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
From 0.9.9 source (collection.fetch):
var method = options.update ? 'update' : 'reset';
collection[method](resp, options);
if (success) success(collection, resp, options);
So by default collection.fetch will cause a reset on the collection, and the resetevent will be fired. If you pass option update:true, and update will be performed.
According to documentation for the new collection.update method, update will trigger the add events for added models, so the following should work:
collection.fetch({
update: true,
success: function (model) {
console.log('fetch')
}
});
Just test and be aware, that the new update method will also trigger remove and changeevents, if models are removed or changed.
You need to remove the commented out line because it won't propagate the add events otherwise (see line 827 of the backbone.js source). So the following should work
collection.fetch({
add: true,
silent: false,
success: function (model) {
console.log('fetch')
}
});
I'm not sure if that's a change from previous versions :)
From what I gather from checking out the Backbone 0.9.9 source, the add -option does nothing with fetch unless you add in the update - option as well. source
So to do something useful with it do the following:
collection.fetch({
add: true,
update: true, // this is necessary as well
//silent: false,
success: function (model) {
console.log('fetch')
}
});
This is also the cause for your problem. When you fetch, the Collection automatically defaults to the reset -function after fetching. reset silences the add -events and opts to just trigger a reset event as is evident from the Backbone.js source
if (models) this.add(models, _.extend({silent: true}, options));
So use the update option if you want the add -events and do not want to empty the collection before adding the new models. If you must have the reset functionality AND the add event, then you might have to write some custom implementation of reset.

Resources