Multiple Backbone.js collection options? - javascript

Intro:
Building in node.js and express on the backend, I am sending a res.json(details) to the localhost:3000/me route containing the users session information.
So that on the client side I can work with that specific user, for example on the client side initialization I write some code like this.
var me = new MeModel();
me.fetch({
success: function(response) {
App.data.me = me;
var messages = new MessagesCollection([], { id: response.get('user_id') });
messages.fetch({
success: function() {
App.data.messages = messages;
App.core.vent.trigger('app:start');
}
});
}
});
You see I fetch the me model and use that to filter the messages in the MessagesCollection.
Question:
In my MessagesCollection I pass options like this.
module.exports = MessagesCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
return '/api/messages/' + this.id;
},
model: MessageModel,
//url: '/api/messages'
});
This is great for getting the desired models when using var messages = new MessagesCollection([], { id: response.get('user_id') });
My question is when somewhere else I run window.App.data.messages.create(Message); this wants to POST to /api/messages/:id when I want to POST to the regular collection?
Theory:
So obviously I would say the best thing is to rethink how I filter the models in the fetch method.
So basically to simplify this question I need to filter the collection upon .fetch() GET... and set no filters upon .create() POST
BTW I am using marionette, maybe that can help with something?

model.url() Returns the relative URL where the model's resource
would be located on the server. If your models are located somewhere
else, override this method with the correct logic. 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.
Delegates to Collection#url to generate the URL, so make sure that you
have it defined, or a urlRoot property, if all models of this class
share a common root URL. A model with an id of 101, stored in a
Backbone.Collection with a url of "/documents/7/notes", would have
this URL: "/documents/7/notes/101"
http://backbonejs.org/#Model-url
So you can define method url at MeModel and generate url there ( if there are no other users - you can just return string "/me" or generate in based on model properties ( for example switch if model has id )

Related

Backbone merge on some attribute, save on id

For an application i need to fetch default values, after which i merge in user specific values. These models are matched by a key attribute.
The defaults have no id the user specifics do have an id.
If i set my idAttribute to key it neatly merges the user specific values into the default models. But when i want to save this model, it will create the PUT url with the key attribute, because of the idAttribute. I want to save to collection.url + id, just as basic Backbone functionality.
Why not override the url? If a user edits to default model, it needs to POST to my REST server, normally a backbone model without an id will do this automatically. By overriding the URL, this doesn't work anymore.
changing the idAttribute before calling .save() doesn't seem to effect it.
Code example:
DefaultWeatherLimits = BackboneCollection.extend({
url: 'limits/default',
comparator: 'displayOrder',
model: WeatherLimit,
initialize: function () {
this.fetch();
}
});
Respond is an array like this (with more info in actual situation):
"[{"key":"MinHurricaneDistance","visualizationHint":"Value","value":300},{"key":"MaxHeadWavesHeight","visualizationHint":"Value","value":300}]"
The model looks like this:
WeatherLimit = Backbone.Model.extend({
idAttribute : 'key'
});
Now , as test add in a User Specific model:
var fake = this.add({id: 99, key: "MinHurricaneDistance", checked: true}, {merge: true});
I want this fake model to save to 'limit/default/99', but it will save to 'limit/default/MinHurricaneDistance'
And i want the MaxHeadWavesHeight model to POST to '/limit/default', it will get an id in return and after i can save it to that id.

Backbone.js adding model retrieved from server to a collection

I would like to know if it possible to add a model to a collection in the following manner.
I tried it and it doesn't work. If I add the JSON [] directly it does. I know this is a little strange but I am trying to get a better understanding of how BB works. Thanks.
var UserModel = Backbone.Model.extend({
urlRoot: '/users',
});
var UserModels = Backbone.Collection.extend({
url: '/users',
model: UserModel
});
var user = new UserModel({id:1});
user.fetch({ // get the model
success: function (user) {
var users = new UserModels();
users.add(user); // add the model to a collection
}
});
The following model is being echoed from the server:
[{"name":"john","email":"john#bb.com"}]
Since the response from the server is an array with a single element, you will need to add a parse function that returns the first element of the array:
var UserModel = Backbone.Model.extend({
urlRoot: '/users',
parse: function(response){
if(response && response.length) return response[0];
}
});
That should allow the model to correctly parse the attributes in this response from the server: [{"name":"john","email":"john#bb.com"}].
By the way, you are creating the users collection in the success function, so it won´t be available outside that function. It might just be the sample code you posted, but you could create the users collection outside and just add the model in the success function:
var users = new UserModels();
var user = new UserModel({id:1});
user.fetch({ // get the model
success: function (user) {
users.add(user); // add the model to a collection
}
});
I have created a fiddle so you can give it a try. (I have used fiddle echo service, so thats why I have changed the url and it is using a POST). If you run it and check the console, you will see the users collection being logged and you can check its models property.

save in server by model or by collection?

I'm confused about send collection or model to the server.
This is my model:
var Person = Backbone.Model.extend({
defaults : {},
initialize : function() {}
});
and this is my collection:
var Usercollection = Backbone.Collection.extend({
model : Person,
url : 'https://api.parse.com/1/classes/_User/'
});
Now, if I would save a model on the server I have first to add in a collection and use save on model or first add in a collection and use save on collection? And least, I have to write an ajax call to post the collection or model in a server?
You should save your model to server.
Save a model: Call save() on model e.g.
var user = new UserModel();
user.save({name: 'SJ', age:'35'}, {
success: function (user) {
// I get a model here with id
}
});
Read these links for more information.
How to save your model data: SO Link
Sample code - by Thomas Davis in his screencast # backbonetutorials.com - Must watch
Server side code for wine cellar app
I have given you the link of server side code to have a look at the APIs to make things more meaningful to you. Hope this helps!
If you want to add the model to the collection after the model is saved, you need to use .create on the collection , which fires the add event on the collection after it gets created..
this.collection.create(model.toJSON() , options);
Use collection.create();
http://backbonejs.org/#Collection-create
Convenience to create a new instance of a model within a collection.
Equivalent to instantiating a model with a hash of attributes, saving
the model to the server, and adding the model to the set after being
successfully created. Returns the new model. ...
var Library = Backbone.Collection.extend({
model: Book
});
var nypl = new Library;
var othello = nypl.create({
title: "Othello",
author: "William Shakespeare"
});

Javascript Backbone model design

Fairly new to JavaScript so it might be a noobish question.
At the moment for my project I'm using NodeJS for my server and Backbone for the client. The client will send a request to the server and the server will send list of files in the server, my aim was to simply return the list of files and when user click on the file it will send another request to the server to load the file.
Currently in the client level my model and collection is defined something like:
app.MyFile = Backbone.Model.extend({
defaults: {
modifiedDate: new Date(),
path: '',
content: '' // content of the file
}
});
var MyFileList = Backbone.Collection.extend({
model: app.MyFile,
url: '/api/files'
});
// create global collection of files
app.MyFiles = new MyFileList();
app.AppView = Backbone.View.extend({
initialize: function () {
// fetch all files
app.MyFileList.fetch();
}
});
// app.js (point of entry)
$(function() {
// Kick things off by creating the **App**.
new app.AppView();
});
And my server code:
var application_root = __dirname,
express = require("express"),
...
app.get('/api/files', function(req, res) {
...
// return file list
}
app.get('/api/files/:id', function(req, res) {
...
// return file content?
}
Since it doesn't make sense to load all files in the directory and send it back to the client, what I did was I created the model in the server and fill up modifiedDate and path while leaving content to null. But the problem now is that how do I fill up the content when user clicks on the file? I'm not sure how to manually send an HTTP request from Backbone View or controller. Or is there any better way of doing this? One way that I can think of is to create another model that only keeps modifiedDate and path but to me this looks very verbose and repetitive.
Given what you have on the client side, you may not need anything more.
app.MyFiles = new MyFileList();
app.MyFiles.fetch().done(function() {
// your collection is fetched but each model's content is empty.
// now, I assume at this point you will show them in some view/views.
});
Now when one of those things is clicked on, you can fetch the content.
var model = app.MyFiles.get(id);
model.fetch().done(function() {
// now the model's content attribute will be set
});
This might work with no more code than what you showed. Because the url a model uses to fetch is created by default by appending the model's id to the end of its collection's url.
So from your server, you return a json array from '/api/files': [{id:1, path:'foo'}, {id:2, path:'bar'}]
Then from '/api/files/1': {id:1, path:'foo', content:'whatever'}
When user clicks the file, you can call backbone's fetch method on the model. Then your model will be filled with data from the server.
Note that for this to be working you should return collection from the server first, where models at least have id's. Every other field will be filled after fetch call. Also, you should override model url, if it differs from standard (which is collection/id).

How to handle custom response in Backbone model

I started integrating backbone in my project. The very first difficulty that i had was response from backend was not JSON Array or not designed for backbone. Here is an example.
//A backbone model
var Person = Backbone.Model.extend({});
// A backbone collection
var PersonCollection = Backbone.Collection.extend({
model : Person,
url: '/people'
});
So consider this, that when I request /people it does not return JSON array of people. Instead it return something like:
{header: "some str", people: ["person", "array", ".."], stats: "something is here" }
The problem with it is backbone is unable to assign this JSON response to models. Is there any tweak that can be done in controller on response. So accessing model can be normal.
Any before/after hook.
FYI: backbone is getting response from server, I can see it under "responseText" key.
Any help is highly appreciated.
Backbone supports this. I ran into the same issue when consuming data from Parse.com. In your case, when you have a /people endpoint that does not return an array, you can override the Collection.parse function to show Backbone how to find the array it is looking for:
var PersonCollection = Backbone.Collection.extend({
model : Person,
url: '/people',
parse: function(resp, xhr) {
this.header = resp.header;
this.stats = resp.stats;
return resp.people;
}
});
If you need to do more in this function, then you should. In a similar way, you can override Model.sync if you need to.

Categories