Custom URL for Create, Update and Delete in Backbone.js - 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().

Related

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.

Why backbone add extra parameter to url path?

I am using backbone.marionette (1.0.0) and node.js (0.10.22). Wondering why backbone add extra parameter when I try to save model data with node.js REST call.
model.js
Backbone.Model.extend ({
urlRoot: function (){
return '/path/' + myApp.companyId;
},
defaults: {
companyId: '',
// other attributes
},
// doesn't use 'id' in model instead companyId
idAttribute: 'companyId'
});
Before view is loaded, I would request model data with myApp.request ('entities:myModel') which issued model.fetch () and node.js backend would fire GET /path/1 route. No issue.
However, when an update button is clicked on the view, this.model.save () would fired PUT /path/1/1. It should be PUT /path/1, with only a single '1' in url path.
view.js:
clicked: function () {
var formData = Backbone.syphon.serialize (this);
this.model.set (formData);
var promise = this.model.save ();
promise.done ().fail ()
}
How can I stop backbone.sync from appending extra parameter to url path? Thanks for taking time out to read this, and I appreciate your help.
You're setting urlRoot incorrectly on the model. It should be
Backbone.Model.extend ({
urlRoot: "path",
// etc
});
Backbone willa dd the ids on it's own.
If you want to specify the ids in your function, use the url function.

backbone: trying to set error message on "this" in failed ajax request

I have a User model in a Backbone application that makes an ajax request. In the error callback, I wish to set an error message to pass to the view. However, if I try do
this.set({errors: result.errors});
I'm told "this" doesn't have a method set. In this case, I believe "this" is the ajax response object (rather than the User model which does have a set method)
Object {url: "/users.json", isLocal: false, global: true, type: "POST", contentType: "application/x-www-form-urlencoded; charset=UTF-8"…}
However, I also tried to do
this.model.set({errors: result.errors});
but it said I can't call "set" of undefined. I'm assuming it doesn't make sense to say "this.model" from within the model, but, as mentioned above, if I just say "this," it refers to the response object.
Is this the wrong way to go about it?
I am assuming you are doing something like this when you are saving your model
model.save({
success: function() {},
error: function() {
this.set({ errors: result.errors });
}
});
If that is the case, then you can change this.set to model.set, and everything will work.
However it doesn't really make that much sense to be storing the error message as a model attribute.
The model will fire an event when its save call fails on the server (check out the backbone events catalogue).
Therefore if you have a view with an attached model, you can tell the view to listen to this error event.
var MyView = Backbone.View.extend({
initialize: function() {
// if your using backbone v0.9.10
this.listenTo(this.model, 'error', this.handleModelError);
// or for earlier versions
this.model.on('error', this.handleModelError, this);
},
handleModelError: function(model, xhr, options) {
// show an error message, or whatever
}
});
var view = new MyView({ model: aModel });
// if the server returns an error, view.handleModelError will be called
aModel.save();
I think this probably loses context. Try using var self = this. Something like:
var self = this;
model.save("author", "F.D.R.",
{error: function()
{
self.model.set({errors: result.errors});
}});

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.

One View connected to multiple models

I have the following problem…
MyView which is connected to two views: TaskModel and UserModel
TaskModel = {id: 1, taskName: "myTask", creatorName: "myName", creator_id: 2 },
UserModel = {id: 2, avatar: "someAvatar"}
The view should display
{{taskName}}, {{creatorName}}, {{someAvatar}}
As you can see the fetch of TaskModel and UserModel should be synchronized, because the userModel.fetch needs of taskModel.get("creator_id")
Which approach do you recommend me to display/handle the view and the two models?
You could make the view smart enough to not render until it has everything it needs.
Suppose you have a user and a task and you pass them both to the view's constructor:
initialize: function(user, task) {
_.bindAll(this, 'render');
this.user = user;
this.task = task;
this.user.on('change', this.render);
this.task.on('change', this.render);
}
Now you have a view that has references to both the user and the task and is listening for "change" events on both. Then, the render method can ask the models if they have everything they're supposed to have, for example:
render: function() {
if(this.user.has('name')
&& this.task.has('name')) {
this.$el.append(this.template({
task: this.task.toJSON(),
user: this.user.toJSON()
}));
}
return this;​​​​
}
So render will wait until both the this.user and this.task are fully loaded before it fills in the proper HTML; if it is called before its models have been loaded, then it renders nothing and returns an empty placeholder. This approach keeps all of the view's logic nicely hidden away inside the view where it belongs and it easily generalizes.
Demo: http://jsfiddle.net/ambiguous/rreu5jd8/
You could also use Underscore's isEmpty (which is mixed into Backbone models) instead of checking a specific property:
render: function() {
if(!this.user.isEmpty()
&& !this.task.isEmpty()) {
this.$el.append(this.template({
task: this.task.toJSON(),
user: this.user.toJSON()
}));
}
return this;​​​​
}
That assumes that you don't have any defaults of course.
Demo: http://jsfiddle.net/ambiguous/4q07budc/
jQuery's Deferreds work well here. As a crude example:
var succesFunction = function () {
console.log('success');
};
var errorFunction = function () {
console.log('error');
};
$.when(taskModel.fetch(), userModel.fetch()).then(successFunction, errorFunction);
You could also pipe the request through using the crude data (remember that fetch, save, create are really just wrappers around jQuery's $.ajax object.
var taskModelDeferred = taskModel.fetch();
var userModelDeferred = taskModelDeferred.pipe(function( data ) {
return userModel.fetch({ data: { user: data.userId }});
});
note: Backbone returns the collection and model in the success / error functions by default on collections and models so if you need this be sure have a reference handy.
I've run into this very same issue with a complex layout that used two models and multiple views. For that, instead of trying to synchronize the fetches, I simply used the "success" function of one model to invoke the fetch of the other. My views would listen only to the change of the second model. For instance:
var model1 = Backbone.Model.extend({
...
});
var model2 = Backbone.Model.extend({
...
});
var view1 = Backbone.View.extend({
...
});
var view2 = Backbone.View.extend({
...
});
model2.on("change",view1.render, view1);
model2.on("change",view2.render, view2);
Then...
model1.fetch({
success : function() {
model2.fetch();
}
});
The point to this is you don't have to do any sophisticated synchronization. You simply cascade the fetches and respond to the last model's fetch.

Resources