When to use URL attribute for both Collection and Model? - backbone.js

I've been following some Backbone.js tutorials and am a bit confused about when to use 'url' inside Model when there is also a Collection which contains the model. For example I see a lot of code that looks like this, in order to synchronize with the server:
var PostModel = Backbone.Model.extend({});
var PostsModel = Bacbone.Collection.extend({
model: PostModel,
url: "/posts"
});
However I also see some where the model also has 'url' property, like so:
var PostModel = Backbone.Model.extend({
url: "/posts"
});
var PostsModel = Bacbone.Collection.extend({
model: PostModel,
url: "/posts"
});
I think I understand the first method, where I interact with the models only through the collections to which they belong, but I am not sure when you would want to have urls for both a model and its collection.

Sometimes you need a model that doesn't belong to any collection.
For example a login/sign in.
var LoginModel = Backbone.Model.extend({
url: "/login"
});
This way you can interact with login view and model just like you would with any other Backbone model, including save() (which posts the login form to the server) and validation.
There is however no login collection, so it doesn't make sense to model that.
Maybe the code you're seeing is something in between. Models are accessed via collection in some places of the application and without collection in others.
If it's a good design is another story, but there's no technical reason to not do it.
edit
As you noticed, using urlRoot is another option, since by default the url attribute is a method that uses urlRoot to construct the relative url.
It can be, however, given a string value to specify the model url directly.

Related

Using multiple models and collections in Marionette/Backbone view

I'm not sure what the right approach here is. I am setting up a simple form. I need a couple of standard HTML input fields and a couple of select inputs. The data is coming from a couple of different sources and I would like to present the models and collections, with their data to the view like so:
Controller:
var registerView = new registrationView.RegistrationForm({
model: userModel,
model2: departmentCollection
});
myApp.SomeRegion.show(registerView);
Can I do this? Or do I need to split the form into separate regions, all with their own model or collection. If so, how do I call the model data in the template. I have not been able to make it work so far. I cannot find any examples of a form with mixed fields coming from different models and collections,
Many thanks
Wittner
You can do it using a composite view:
var registrationView.RegistrationForm = Marionette.CompositeView({
// ...
});
var my View = new registrationView.RegistrationForm({
model: userModel,
collection: departmentCollection
});
See https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.compositeview.md and https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.collectionview.md (a composite view inherits behavior from a collection view).

Returning nested model from backbone via signalr drops attribute property

I am working on a backbone/signalr POC. I have very simple models working and I can create them client side and retrieve them via signalr.
The problem is this:
If I create a client side version of a model with a nested model I can access the attributes like this:
model.attributes.nestedModel.attributes.attributeName
When I retrieve the model from signalr via
model.fetch()
the model comes back but now to access the nested model properties I need to use
model.attributes.nestedModel.attributeName
the attributes level on the nested model is dropped, so this causes template rendering to fail
How do I get around this? Am I doing something wrong? I am new to signalr/backbone.
BTW, I am using the backbone.signalr nuget package.
Thanks.
this is because when you are using fetch(), the server returns only one JSON object with the model attributes and the attributes of the nested models. for example, the server returns:
{
id: "1",
name: "Model",
nestedModel: {
id: "12",
name: "nestedModel"
}
}
backbone is not smart enough to figure out that nestedModel is actually a "model". It treats "nestedModel" as an attribute on Model. (it's just a regular JSON object, not a backbone object)
that's why this:
model.attributes.nestedModel.attributes.attributeName
does not work.
to make it work, you have to instantiate nestedModel as a Backbone Model. so after fetch is done: (assuming your nestedModel is an instance of NestedModel)
model.fetch().done(function() {
model.set('nestedModel', new NestedModel(model.get('nestedModel')));
});
You can make backbone does this for you automatically by overwriting the parse() method.
in your model:
var NestedModel = Backbone.Model.extend({
//your nested model methods
});
var Model = Backbone.Model.extend({
//do other model stuff
parse: function(response) {
response.nestedModel = new NestedModel(response.nestedModel);
return response;
}
});
this should make your statement work.
but usually I'd use
model.get('nestedModel').get('attributeName')
for more info about parse, see here: http://backbonejs.org/#Model-parse
and to apply this pattern in all other models with more flexibility, you probably wanna read this:
http://www.devmynd.com/blog/2013-6-backbone-js-with-a-spine-part-2-models-and-collections

Backbone.js: Natively passing attributes to models when fetched with a collection

Let say you are defining a Backbone.js Model. From the documentation we have ...
new Model([attributes], [options])
This seems great for passing some default attributes to a model. When passed the model automatically inherits those attributes and their respective values. Nothing to do. Awesome!
On they other hand lets say we have a Collection of that model.
new Backbone.Collection([models], [options])
Okay, cool we can pass some initial models and some options. But, I have no initial models and no options I need to pass so let's continue. I am going to fetch the models from the server.
collection.fetch([options])
Well I don't have any options, but I want to pass some attributes to add to each models as it is fetched. I could do this by passing them as options and then adding them to the attributes hash in the initialize for the model, but this seems messy.
Is their a Backbone.js native way to do this?
You can pass the attributes as options to fetch and over-ride the collection's parse method to extend the passed options (attributes) on the response.
The solution would look like the following:
var Collection = Backbone.Collection.extend({
url:'someUrl',
parse:function(resp,options) {
if (options.attributesToAdd) {
for (var i=0;i<resp.length;i++)
_.extend(resp[i],options.attributesToAdd);
}
return resp;
}
});
Then to add attributes when you call fetch on the collection, you can:
var collection = new Collection();
collection.fetch({
attributesToAdd:{foo:'bar',more:'foobar'}
});
You may have to tweak the code a bit to work with your JSON structure, but hopefully this will get you started in the correct direction.

Backbone model singleton

I am new to Backbone.js and I have the following problem: I have multiple views that uses the same model. I do not want to re-fetch model on every view render, but I want to fetch it only once and then when view is rendered use that instance/data.
My example: I have 3 views for user. One is some user statistics, another user info and third user profile. After login user lands on user profile view and there I fetch the user model but how could I then pass this model reference around or even better how could I access that model data from different views?
I hope I am not doing any anti-pattern here. I have seen a lot of examples with binding events to model change and then rerender all the views but that is not my case. I am using backbone.js with require.js and underscore template engine.
Just return the instantiated model:
define(function (require) {
var MyModel = Backbone.Model.extend({})
return new MyModel()
});

How to update model in collection?

I have the following Backbone.js collection:
var Tags = Backbone.Collection.extend({
url: "/api/v1/tags/"
}),
How do I update one of the models in the collection so that it posts to /api/v1/tags/id and saves the data for that model.
So if I change name of model with id 2 in the collection
It should PUT to
/api/v1/tags/2 with the following data:
name: new name id: 2
I've also recently wanted to update particular model in the collection. The problem was that if I did use just model.save it didn't update the collection. The goal was to change the model in collection, change it on the server, update collection accordingly and not use the sync method. So for example I have my variable collection and I want to change the model with id = 2. So the first thing, I will create an instance model, like this: var model = collection.get(2)Then I will update the attributes on this particular model:model.set({name: 'new name'})Then I will save it to the server:model.save({}, {url:'/api/v1/tags/'+model.get('id')})Then we have to update collection accordingly to the changes:collection.set({model},{remove: false})set method - it's a 'smart' update of the collection with the list of the models you passed in parameters. remove: false parameter - it's a restriction for a collection to remove existing models in collection. More here.
The first thing you can miss is that in your corresponding Tag model you'll need to set "urlRoot" to match the Collection's "url". Otherwise it doesn't know about the collection at all:
var Tag = Backbone.Model.extend({
urlRoot: "/api/v1/tags"
});
var Tags = Backbone.Collection.Extend({
model: Tag,
url: "/api/v1/tags"
});
This is useful if you want to save the tag separately:
var tag = collection.get(2);
tag.set({key: "something"});
tag.save(); // model.save works because you set "urlRoot"
On the collection, "create()" is also "update()" if id is not null. That's not confusing. :) Therefore, this is pretty much equivalent to the previous sample:
collection.create({id: 2; key: "something"});
This will update the existing tag with id=2 and then trigger a PUT.
This is an ancient question; answering because I was searching for the same answer--you've probably long since solved this problem and moved on. :)
You can pass variables to the save method. It accepts all the options which jQuery's ajax method uses (unless you overrided Backbone.Sync)
You could do something like:
model.save( { name:'new name' } );
The id and PUT method will automatically be added by Backbone for you.

Resources