Changing 'url' property of Backbone Model and Collection - backbone.js

I'm writing a custom sync for Backbone and the name url for the sync isn't an obvious choice. I'd like to make it something like point so instead of:
MyAppModel = Backbone.Model.extend({
url: '/some/api'
});
I can use...
MyAppModel = Backbone.Model.extend({
point: 'some/reference`
});
Is there any way to (without modifying Backbone) make it so url = point?

This should work:
url = function(){return this.point };
But why do you want to 'rename' the url property? Just for semantics?

Related

How to fetch the model in a view with a dynamic url defined in a view

I am using backbone.js in my app. My model named MyModel is
Backbone.Model.extend({
urlRoot: 'home'
});
Need to fetch model with url "home/abc/xyz" where "abc" and "xyz" are dynamic in my view. I did the following
var model = new MyModel({id:'abc/xyz'});
model.fetch();
but its not working.
It goes to url "home/abc?xyz".
How should i solve this issue?
Here is the url function of Backbone.Model which is responsible for such kind of behavior in Backbone:
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
}
As you can see encodeURIComponent(this.id) will encode your id, so you can't pass and '/' -> '%2F'.
You always can rewrite this function, but I guess it's not the best idea, cause it can break another things.
I can suggest you another approach:
Just define urlRoot of your model as function and there do your job:
var yourModel = Backbone.Model.extend({
defaultUrl: 'home',
urlRoot: function () {
return defaultUrl + this.get('yourUrlAttribute');
}
});
Just pass the yourUrlAttribute as model attribute when creating it and fetch the model.
Having in mind this approach and that Backbone.Model will append encoded 'id' as the last part of URL (when you fetching model) you can get what you want.

Backbone, requirejs, multiple views, one collection

Below you can see four basic requireJS files. How can I have multiple Backbone.js views that will all share one collection which has been initiated and fetched elsewhere?
Please note
I am aware I can pass the collection in App.js however I would like to refrain from doing so since I will probably have many collections that will need to be used in many views, and I don't want to pass each of them in App.js.
Collection.js
return Backbone.Collection.extend({
url: '...'
});
App.js
var collection = new Collection();
$.when(collection.fetch()).done(function(){
new View1();
new View2();
});
View1.js
define(['Collection'], function(Collection){
return Backbone.View.extend({
initialize: function(){
console.log('Need the initiated, fetched collection here...');
});
});
});
View2.js
define(['Collection'], function(Collection){
return Backbone.View.extend({
initialize: function(){
console.log('Need the initiated, fetched collection here...');
});
});
});
Quick answer: RequireJS runs function body code once and the return statement alone multiple times. You can, hence, create a Collection Instance and return that to every person who requires it.
Collection.js
var CollectionConstructor = Backbone.Collection.extend({
url: '...'
});
var CollectionInstance = new CollectionConstructor();
return CollectionInstance;
Alternatively, if you want more than one instance of CollectionInstance running around, and don't want to pass it to the views, I don't believe anything short of global variables will help. That would look like:
Globals.js
var AppGlobals = AppGlobals || {};
AppGlobals.oneSet = new Collection ();
AppGlobals.anotherSet = new Collection ();
You can now have your views depend on Globals.js and access them from here. Depending on your use, either of these two should work. Keep in mind that in the second approach, your Collection.js is unmodified.

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';
}

Is there an equivalent to parse to use before syncing with the server?

In my backbone model, I parse the response from the server:
var MyModel = Backbone.Model.extend({
urlRoot: "/users",
parse: function(response){
var data = {};
data.id = reponse.userDetails.id;
data.name = response.userDetails.firstname + " " + response.userDetails.lastname;
data.description = response.userDetails.description;
return data;
}
});
var myModel = new MyModel({id: 1});
myModel.fetch();
The views that use this model can manipulate it, for example, if the user were to click on the view to "select" it, it would update the model...
myModel.set({selected: true});
...and the view would re-render based on the model's change event and highlight the "selected" user.
When it comes time to save the model to the server, how do I only send the attributes the server wants? and ignore the attributes which were added through user interaction.
OR
Should the data model always reflect what the server returns? If so, is there a better way to store the user interactions (whether the view is "selected")? Should it be a separate model than the actual data model?
Thanks
The model doesn't need to mirror the data on the server if that doesn't make sense for your application.
For the model's attributes, if you don't need to render those attributes in a template, then you can just override model.toJSON() to only serialize the attributes you want sent to the server. Be careful though, in this case if you are rendering your template (or anything else) using this.model.toJSON() then it will also be affected. If that's a problem then you can override model.sync() instead and manipulate the data passed in before sending it to Backbone.sync. For example:
var myModel = Backbone.Model.extend({
sync: function (method, model, options) {
// remove the unwanted attributes. Something like...
options.attrs = _.pick(model.attributes, 'attribute1', 'attribute2', 'attribute3');
return Backbone.sync.call(this, method, model, options);
}
});
Overriding model.toJSON as suggested by mu_is_too_short worked nicely for me.
In the model
function() {
var json = Backbone.Model.prototype.toJSON.call(this);
json.ExtendedFieldData = JSON.stringify(json.ExtendedFieldData);
return json;
},
We use model.attributes for templates.

Backbone.js - fetch data and display

I'm just beginning out on Backbone.js. Here's my code.
$(function(){
//Backbone Model
var Cat = Backbone.Model.extend({});
// create a collection
var CatCollection = Backbone.Collection.extend({
model: Cat,
url: 'http://localhost/cats/index.php/cats/index'
});
var catCollection = new CatCollection();
catCollection.fetch();
// Backbone view
var CatView = Backbone.View.extend({
el: $("#contents"),
initialize: function() {
this.render();
},
render: function() {
this.el.html(catCollection);
}
});
var catView = new CatView();
});
What I am doing is.
Create a backbone model
Create a collection using the model I created.
Fetch data from MySQL database - this returns a JSON data object.
Display the fetched data in the div "#contents".
On google Chrome, I can see that the "fetch()" method works, because I can see my JSON object returned as
[{"id":"1","name":"stella","age":"5"},{"id":"2","name":"Max","age":"2"}]
But if I do "alert(catCollection)" after the fetch, it displays "[object] [object]".
What is the best way of displaying this?
You should use the templates in JST array.
$(this.el).html($(JST["comments/item"](model.toJSON())));
"comments\item" is the template path and name
If you are using Rails, just use the Jammit and write templates with ERB (default) or Jade
The backbone.js documentation give an example like so:
alert(JSON.stringify(catCollection));
http://backbonejs.org/#Collection-toJSON
Use console.log.

Resources