So, I'm just started using backbone.js and what I have some troubles understanding how to define two collections where one of them is within a model of the other one.
The REST API looks something like this:
/sites <--- one collection
/sites/123/entities <--- another collection
/sites/123/entities/abc <--- a specific entity within a specific site
This is how the Sites collection is defined:
var ROOT = 'http://localhost:5000';
Site = Backbone.Model.extend({
defaults: {
id: "default",
description: "My site"
}
});
Sites = Backbone.Collection.extend({
model: Site,
url: ROOT + "/sites"
});
How would the Entities collection and model look like to achieve this?
This is how I imagine it would be, though haven't tested it:
Entity Model/Collection:
var ROOT = 'http://localhost:5000';
Entity = Backbone.Model.extend({
defaults: {
id: "default",
description: "My Entity"
}
});
Entities = Backbone.Collection.extend({
model: Entity,
ownUrl: '/entities',
site: {},
url: function() {
return (this.site? this.site.url() : '') + this.ownUrl;
},
initialize: function(options) {
this.site = options.site;
}
});
Site Model/Collection:
Site = Backbone.Model.extend({
defaults: {
id: "default",
description: "My site"
}
});
Sites = Backbone.Collection.extend({
model: Site,
url: ROOT + "/sites",
});
Example of setting up:
// New collection of site
var site1 = new Site({id: 'site1', description: "This is site 1"});
var site2 = new Site({id: 'site2', description: "This is site 2"});
// Add entities to sites
var entityCollection1 = new Entities([
{id: 'entity1'},
{id: 'entity2'}
], {site: site1});
// Collection of sites
var mySites = new Sites([site1,site2]);
Edited:
According to Backbone documents, from version 1.1, this.options is not attached automatically anymore. Sorry I was still refer to the 0.9.x code.
In 1.1, Backbone Views no longer have the options argument attached as
this.options automatically. Feel free to continue attaching it if you
like. http://backbonejs.org/#changelog
For the url, my bad, it was supposed to be this.site.url() because it's a model's function which will take the default value as [collection.url/modelId].
In this case, it should return 'site/1' for Model site1 (i.e. 'site/2') then you append whatever your entity collection url will be.(ex: '/entities').
This makes your Entities collection url for site 1 become: /site/1/entities. Any model in this collection will have url /site/1/entities/[modelId]
Related
My problem is that I am just starting out with Backbone.js and are having trouble wrapping my head around a complex problem. I want to save a form that have infinite fields, and some of the fields also needs to have infinite options. I'm just worried I might have started at the wrong end with a JSON response, instead of building the models/collections first. Here is a short pseudocode of what I try to achieve.
id:
parent: <blockid>
fields: array(
id:
title:
helpertext
options: array(
id:
type:
value:
)
)
Currently I am working with a faked JSON response from the server, which I built from scratch, and now I want to divide it into models and collections on the client side.
//Fake a server response
var JSONresponse = {
"formid":"1",
"fields":[
{
"fieldid":"1",
"title":"Empty title",
"helper":"Helper text",
"type":"radio",
"options":[
{
"optionid":"1",
"value":"Empty option.."
},
{
"optionid":"2",
"value":"Empty option.."
}
]
},
{
// fieldid2
}
]
};
The idea is to add fields as I see fit, and then if the field type is radio/checkbox/ul/ol there must also be an "options" array within the field.
My work so far:
var app = {};
app.Models = {};
app.Collections = {};
app.View = {};
app.Models.Option = Backbone.Model.extend({
});
app.Collections.Options = Backbone.Collection.extend({
model: app.Models.Option
});
app.Models.Field = Backbone.Model.extend({
options: new app.Collections.Options()
});
app.Collections.Fields = Backbone.Collection.extend({
model: app.Models.Field
});
app.Models.Form = Backbone.Model.extend({
formid : "1",
fields: new app.Collections.Fields(),
initialize: function() {
}
});
How do I split up my JSON response into all these models and collections?
(Perhaps I should re-evaluate my approach, and go for something like form.fieldList and form.optionList[fieldListId] instead. If so, how would that look like?)
Edit: Here is a little jsfiddle after many fixes, but I still don't really know how to make the inner options list work.
The easiest solution would be using Backbone Relational or Backbone Associations.
The documentation should be enough to help you get started.
If you don't want to use a library you could override the parse function on the Form model.
app.Models.Form = Backbone.Model.extend({
defaults: {
fields: new app.Collections.Fields()
},
parse: function(response, options) {
return {
formid: response.formid,
fields: new app.Collections.Fields(_.map(response.fields, function(field) {
if (field.options) {
field.options = new app.Collections.Options(field.options);
}
return field;
}))
};
}
});
Now if you fetch a form from the server, the response will be parsed into an object graph of models and collections.
form.get('fields') will return an app.Collections.Fields collection. form.get('fields').first().get('options') will return an app.Collections.Options collection, if any options exist.
Also, you could create the form model like this:
var form = new app.Models.Form(JSONresponse, {
parse: true
});
This would result in the same object structure.
It's quite hard to handle the case of nested models and collections right in plain Backbone.
Easiest way of handling this will be something like this:
var Option = Nested.Model.extend({
idAttribute : 'optionid',
defaults : {
optionid : Integer
value : ""
}
});
var Field = Nested.Model.extend({
idAttribute : 'fieldid',
defaults : {
fieldid : Integer,
title : "",
helper : "",
type : "radio",
options : Option.Collection
}
});
var Form = Nested.Model.extend({
idAttribute : 'formid',
defaults : {
formid: Integer,
fields: Field.Collection
});
https://github.com/Volicon/backbone.nestedTypes
And that's it. Yep, you'll get direct access to the attributes as free bonus, just form.fields.first().options.first().value, without that get and set garbage.
From reading a textbook called "Developing Backbone.js Applications" by Addy Osmani and trying out a practical example on it, I can see that all elements of the Backbone.Collection class can have elements of different types. A class extending Backbone.Collection can also hold different types of elements, like all elements don't have to be consistent.
Let me show you the code I'm using for my example:
var ToDosCollection = new Backbone.Collection();
ToDosCollection.add([
{ title: 'go to Jamaica.', completed: false },
{ task: "aldvjalkdgj", level: 3, timeDue: "6:00 PM Today" }
]);
console.log('Collection size: ' + ToDosCollection.length);
ToDosCollection.reset([
{ slogan: "Curse you, Sephiroth.", population: 5500,},
{ project: "Final Fantasy X" },
{ amount: 500 }
]);
// Above logs 'Collection reset.'
console.log('Collection size: ' + ToDosCollection.length);
alert(JSON.stringify(ToDosCollection.get(0)));
var GitHubRepository = Backbone.Model.extend({
defaults: {
nameOfRepository : "",
owner : "",
members : [],
dateCreated : "",
commits: 0,
additions: 0,
deletions: 0,
},
initialize : function(){
this.on("change", function() {
console.log("CHANGE DETECTED");
});
this.on("change:nameOfRepository", function(){
console.log("The name of the repository has been changed.");
});
this.on("change:owner", function(){
console.log("The owner of the repository has been changed.");
});
this.on("change:members", function(){
console.log("The members this repository belongs to have been changed.");
});
this.on("change:dateCreated", function(){
console.log("The date this repository was created has been changed.");
});
this.on("change:commits", function(){
console.log("The # of commits this repository has have been changed.");
});
this.on("change:additions", function(){
console.log("The # of additions this repository has have been changed.");
});
this.on("change:deletions", function(){
console.log("The # of deletions this repository has have been changed.");
});
}
});
var GitHubRepositoryCollection = Backbone.Collection.extend({
model: GitHubRepository
});
var newCollection = new GitHubRepositoryCollection();
newCollection.add(
{newObject: new GitHubRepository()},
{title: "Rabbits"},
{elementNumber: 5}
);
Am I understanding this correctly? Or, for some insight, how does this actually work? To me, I feel like I am making this analogous to an instance of the ArrayList in Java that supports Object objects.
EDIT: Considering that I have gotten at least one negative vote on this question, I would like to ask, "How does ItemView in Backbone.js work?"
In order to render itemview for each model in the collection i guess it is better to keep each model analogous.Otherwise you will have to build itemview for each model differently which will increase you overheads to write code which is not good because we are supposed to use backbone in order to render the views which are to be rendered for same data in same way.if you want to render different itemview it is always better to have different composite or collection views which will automate your task of rendering the itemview which eventually will reduce your overheads.
I am trying to render a view for a collection using Backbone.View. But I cannot render the comment view to show the individual comments as a list inside the comments view. Only comments form is rendered. when visited the url of the collection from the address bar the below array is returned as I have written it on the server side code with express.
What can be the issue here I cannot manage to fix? It seems very natural to achieve it with this code, but it is certain that I am missing something. General issue is I am stuck at such detailed points although I can learn a say mvc framework, Backbone, Node, express etc.
CommentsView:
var CommentsView = Backbone.View.extend({
initialize: function (options) {
this.post = options.post;
this.collection = new Comments({post: this.post});//injecting collection from this views options, was injected to this view form router.
this.collection.on('add', this.addComments, this);
},
addComments: function (comment) {
this.$el.append(new CommentView({ model: comment }).render().el);
},
render: function () {
this.$el.append("<h2> Comments </h2>");
this.$el.append(new CommentFormView({post: this.post}).render().el);
return this;
}
});
This is the array returned when the url of collection is visited form the address bar:
[
{
"_id": "547e36df0ca19b2c1a6af910",
"postId": "547d00e30ca19b2c1a6af90b",
"name": "comment one",
"date": "Mon 21 Oct 2014",
"text": "gibberjabber"
}
]
Router method when the route to the comments of the related post is routed to:
comments: function(_id) {
var csv = new CommentsView({ post: this.posts.findWhere( {_id: _id} ) });
this.main.html(csv.render().el);
}
I think it could have something to do with your constructor function for this.collection. When creating a Collection, you should pass in the array as the first parameter and object literal with the options as the second (if you didn't define it when creating the collection class. What I'm thinking is that the "add" event on the collection isn't getting fired so the comments are not being rendered.
var Comments = Backbone.Collection.extend({
model: Post
});
this.collection = new Comments(posts)
I'm guessing that posts is just an array of models
I'm working on a Backbone app, but everything I've read so far is either about displaying a list of items (a TODO list for example) or a single item.
Right now I have users, each user has a list of skills (pretty much like any game). I can easily get all users or a single user, the same for the skills but what if I want to get all the skills for a given user? How would I do that?
I thought about just adding a property to the users with a new instance of a collection, something like this:
var Users = Backbone.Model.extend({
skills: new Skills({ user: this })
});
var Skills = Backbone.Collection.extend({
model: Skill,
url: '/someUrl',
initialize: function (options) {
// fetch all skills from an user
this.fetch({ data: { user: options.user.get('id') } });
}
});
But I don't have much experience with Backbone and I don't really like the idea of that, also the request would look something like /someUrl?user=1 which I'd rather avoid, /someUrl/user/1 would be much better.
I've also noticed BackboneRelational but I haven't really tried it, it seems a bit of an overkill for my problem, but maybe I'm wrong.
What approach should I take to fetch all of my users skills? Thanks in advance.
I highly recommend you to checkout this post, sure you will find an answer. If short you may have following approach without any additional plugins and build nested model :
expect following json:
{
name: 'Gorbachov',
age: '75',
skills: [
{
name: 'kalashnikov'
},
{
name: 'vodka'
},
{
name: 'balalaika'
}
]
}
lets update User model:
User = Backbone.Model.extend({
initialize: function(){
var skills = this.get("skills");
if (skills){
this.skills = new Skills(skills);
this.unset("skills");
}
}
})
Then create SkillsCollection:
SkillsCollection = Backbone.Collection.extend({
model: Skill
})
and Skill model:
Skill = Backbone.Model.extend({
defaults: {
name: 'unnnamed'
}
})
i'm working on a project in backbone and are having some problems when trying to create a new model.
I have an application with two different models where one depends on another.
My models are Books and Authors, and to create a book I need the id of the author.
This is where my problem lies, I have no idea how to get this using backbone?
if I'm not precise enough please ask.
Thank you!
How about something like this:
// create Book model
var book = Backbone.Model.extend({
defaults: {
Title: "",
Desc: "",
Author: 0
}
});
// create Author model
var author = Backbone.Model.extend({
defaults: {
Name: "",
ID: 1
}
});
var markTwain = new author({
Name: "Mark Twain"
});
var huckleberryFinn = new book({
Author: markTwain.get("ID")
});
console.log(huckleberryFinn);
I think what you are looking for is a subcollection. You will end up having an authors collection holding the Autors models. And then you might attach a books collection to every author which holds the book models. Untested proposal:
var Books = Backbone.Collection.extend({
initialize: function(options){
this.author = options.author;
var authorid = this.author.get("id");
}
});
var Author = Backbone.Model.extend({
initialize: function(){
this.books = new Books( {author:this} );
}
});