I'm looking for the correct backbone structure to achieve the following:
Two server APIs:
GET api/event/4 : returns the event object with id 4.
GET api/event/4/registrations : returns the list of registration objects belonging to event with id 4
I want a view displaying the event object and the list of registrations.
This is very straightforward but I cannot figure out how to organize my Event and Registration models.
Should I use backbone-relational?
My Event model is currently like this:
(the collection is expected to contain the next 10 events from now).
How should I define my Registration model and how will I initialize it, knowing that it is always in the context of an Event model?
var app = app || {};
app.EventModel = Backbone.Model.extend({
urlRoot: app.API_server + 'event'
});
app.EventCollection = Backbone.Collection.extend({
model: app.EventModel,
url: app.API_server + 'event',
initialize: function(){
dt = new Date();
start_dt = dt.toISOString();
this.fetch({
data: {limit:10, start_dt:start_dt},
error: function (model, response, options) {
if(response.status == '403') {
app.Session.logout();
}
}
})
}
});
Make a collection for the registration and use the url property as a function. By default, the urlRoot of the models of the RegistrationCollection will be the url of the collection with their id appended.
app.RegistrationCollection = Backbone.Collection.extend({
url: function() {
return app.API_server + 'event/' + this.id + '/registrations';
},
initialize: function(models, options) {
options = options || {};
this.id = options.id;
}
});
Then, on EventModel initializing, add a RegistrationCollection as a property, passing the event id as an option to the collection.
app.EventModel = Backbone.Model.extend({
urlRoot: app.API_server + 'event',
initialize: function() {
this.registrations = new app.RegistrationCollection(null, {
id: this.id
});
}
});
Remove the fetch from the init, you want to make your collection reusable.
app.EventCollection = Backbone.Collection.extend({
model: app.EventModel,
url: app.API_server + 'event',
});
Fetch inside the view or the router, depending on where it makes more sense for your app.
var EventView = Backbone.View.extend({
initialize: function() {
this.collection = new app.EventCollection();
var dt = new Date(),
start_dt = dt.toISOString();
// this should be here, outside of the collection.
this.collection.fetch({
data: { limit: 10, start_dt: start_dt },
error: function(model, response, options) {
if (response.status === 403) {
app.Session.logout();
}
}
});
},
});
Related
Sorry for my bad English. Tell me why the following happens:
I have some backbone collection:
var Background = window.Models.Background = Backbone.Model.extend({});
var Backgrounds = window.Models.Backgrounds = Backbone.Collection.extend({
model: window.Models.Background,
url: '/backgrounds/',
initialize: function() {
this.fetch({
success: this.fetchSuccess(this),
error: this.fetchError
});
},
fetchSuccess: function( collect_model ) {
new BackgroundsView ({ collection : collect_model });
},
fetchError: function() {
throw new Error("Error fetching backgrounds");
}
});
And some view:
var BackgroundsView = window.Views.BackgroundsView = Backbone.View.extend({
tagName: 'div',
className: 'hor_slider',
initialize: function() {
this.render();
},
render: function() {
console.log(this.collection);
this.collection.each( function (background) {
console.log(background);
//var backgroundView = new BackgroundView ({ model: background });
//this.$el.append(backgroundView.render().el);
});
}
});
now i creating collection
var backgrounds = new Models.Backgrounds();
but when I must render this view, in the process of sorting the collection its length is 0, but should be two. This log I see at console. How is this possible? What am I doing wrong??
You are creating the view before the collection fetch is successfull. Your code should be:
initialize: function() {
this.fetch({
success: this.fetchSuccess,
//------------------------^ do not invoke manually
error: this.fetchError
});
},
fetchSuccess: function(collection, response) {
new BackgroundsView ({ collection : collection});
},
You should let backbone call fetchSuccess when the fetch succeeds. Right now you're invoking the funcion immediately and passing the return value undefined as success callback.
This looks like a wrong pattern. Your data models shouldn't be aware of/controlling the presentation logic.
You have a view floating around without any reference to it. You should be creating a view instance with reference(for example from a router, or whatever is kick starting your application) and passing the collection to it. Then fetch the collection from it's initialize method and render after the fetch succeeds. Collection can be referenced via this.collection inside view.
Alternatively you can fetch the collection from router itself and then create view instance. Either way collection/model shouldn't be controlling views.
If the code is structured in the following way, the problem is solved. It was necessary to add a parameter reset to fetch.
var Background = window.Models.Background = Backbone.Model.extend({});
var Backgrounds = window.Models.Backgrounds = Backbone.Collection.extend({
model: window.Models.Background,
url: '/backgrounds/',
initialize: function() {
this.fetch({
reset : true,
});
}
});
var BackgroundsView = window.Views.BackgroundsView = Backbone.View.extend({
tagName: 'div',
className: 'hor_slider',
initialize: function() {
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.collection.each( function (background) {
var backgroundView = new BackgroundView ({ model: background });
this.$el.append(backgroundView.render().el);
}, this);
$('#view_list').empty();
$('#view_list').append(this.$el);
return this;
}
});
I'm using backbone and I'm very new at it, I have a list of products sizes and a list of quantities / prices. When someone selects a different product size, I use backbone to do an ajax call to the server to get me an updated price list.
I'm struggling to get the save function to work so that I can return the updated collection. I will have to pass back a couple params, but for the time being, I'm just trying to get it to save to the backend. I've read save can be used to automatically setup the ajax request.
I'd also only like this to load the template when the li element is clicked, not on page load.
My code
var models = {};
models.PriceModel = Backbone.Model.extend({
})
models.PriceList = Backbone.Collection.extend({
initialize: function(options) {
this.productId = options.productId;
},
model: models.PriceModel,
url: function() {
return '../product/pricing/' + this.productId + '.json'
}
});
View
var PriceView = Backbone.View.extend({
el: '#product-module',
template: Handlebars.compile($("#priceTemplate").html()),
events: {
"click #product-dimensions li": "dimensionClicked",
},
initialize: function(){
this.listenTo(this.collection, 'add', this.render);
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.$el.find('#product-quantities').html( this.template(this.collection.toJSON()));
},
dimensionClicked: function(event, callback){
this.collection.save({},{
success: function(model, data){
console.log('success')
this.collection.fetch();
},
error: function(model, response) {
console.log('error! ' + response);
}
});
},
});
Page
<script>
var prices = new models.PriceList({productId:${productInstance.id}});
var priceView = new PriceView({collection: prices});
<%-- prices.fetch({reset: true});--%>
</script>
The error I'm getting.
TypeError: this.collection.save is not a function
this.collection.save({},{
How do I pass back a couple of params and then refresh the template?
The solution was to use
this.collection.fetch({data: {customParm : searchData}, reset: true});
For some reason, I am getting a TypeError in my JavaScript regarding a supposed Backbone model object for which I am trying to call "model.destroy()":
Here's my Backbone code:
var Team = Backbone.Model.extend({
idAttribute: "_id",
urlRoot: '/api/teams'
});
var TeamCollection = Backbone.Collection.extend({
model: Team
});
var teamCollection = new TeamCollection([]);
teamCollection.url = '/api/teams';
teamCollection.fetch(
{
success: function () {
console.log('teamCollection length:', teamCollection.length);
}
}
);
var UserHomeMainTableView = Backbone.View.extend({
tagName: "div",
collection: teamCollection,
events: {},
initialize: function () {
this.collection.on("reset", this.render, this);
},
render: function () {
var teams = {
teams:teamCollection.toJSON()
};
var template = Handlebars.compile( $("#user-home-main-table-template").html());
this.$el.html(template(teams));
return this;
},
addTeam: function (teamData) {
console.log('adding team:', team_id);
},
deleteTeam: function (team_id) {
console.log('deleting team:', team_id);
var team = teamCollection.where({_id: team_id}); //team IS defined here but I can't confirm the type even when logging "typeof"
console.log('team to delete', typeof team[0]);
console.log('another team to delete?',typeof team[1]);
team.destroy({ //THIS FUNCTION CALL IS THROWING A TYPEERROR
contentType : 'application/json',
success: function(model, response, options) {
this.collection.reset();
},
error: function(model, response, options) {
this.collection.reset();
}
});
}
});
So I am fetching the data from the node.js server, and the server is returning JSON. The JSON has cid's and all that jazz, so those objects were once Backbone models at some point.
I just don't know why the type of team would not be a Backbone model.
Any ideas?
.where returns an array. You need to use .findWhere instead.
Or call destroy for every model in the resulting array.
.where({...}).forEach(function(model){
model.destroy();
});
Here my problem, I want to change my model dynamically (change dynamically a variable in my model when the collection is instantiated).
So here my code :
define(['backbone'], function (backbone) {
var MyModel = Backbone.Model.extend({
initialize: function() {
var that = this;
var likes;
var UrlGetLike = "https://api.facebook.com/method/fql.query?query=select%20like_count%20from%20link_stat%20where%20url=%27https://www.facebook.com/pages/Stackoverflow/1462865420609264%27&format=json";
$.getJSON( UrlGetLike, {
format: "json"
})
.done(function(data) {
likes = data[0].like_count;
that.set({
'likes' : likes
});
});
},
});
return MyModel;
});
But the data are not updated, MyModel is returned before the .done() finished ..
I try this too :
define(['backbone'], function (backbone) {
var MyModel = Backbone.Model.extend({
initialize: function() {
var that = this;
var likes;
var UrlGetLike = "https://api.facebook.com/method/fql.query?query=select%20like_count%20from%20link_stat%20where%20url=%27https://www.facebook.com/pages/Stackoverflow/1462865420609264%27&format=json";
$.getJSON( UrlGetLike, {
format: "json"
})
.done(function(data) {
likes = data[0].like_count;
that.set({
'likes' : likes
});
that.returnn;
});
},
returnn: function(){
return this;
}
});
});
But I got this error Cannot read property 'prototype' of undefined, because I fired
var collection = new Collection({collection : MyModel});
before MyModel is return (I think)
If anyone have a solution or something to help me, it would be appreciate :).
You can fetch the info for each model in the collection after its creation (it's actually a bad thing to fetch data in initialize method as this method was not created for that purpose. It's better to call a fetch method for a model explicitly (in our case let's call it fetchLikes))
var MyModel = Backbone.Model.extend({
fetchLikes: function () {
var UrlGetLike = "https://api.facebook.com/method/fql.query?query=select%20like_count%20from%20link_stat%20where%20url=%27https://www.facebook.com/pages/Stackoverflow/1462865420609264%27&format=json";
$.getJSON(UrlGetLike, {
format: "json"
}, _.bind(function (data) {
likes = data[0].like_count;
that.set({
'likes': likes
});
}, this));
}
});
var Collection = Backbone.Collection.extend({
model: MyModel
})
var collection = new Collection();
//.. insert models in the colleciton ..
collection.forEach(function (model) {
model.fetchLikes();
})
Bare in mind that you are doing ajax requests in for-loop that is considered a bad practice.
Do it only if you have no way to get the whole data in one request.
i have a problem with backbone.js. I'm creating a frontend for an existing api, for me unaccessable. The problem is that when I try to add a new model to a collection, i can see in my firebug that every time backbone tries to create the model it appends the attribute name to the url.
Example:
default url = /api/database
when i perform a GET = /api/database
when i perform a GET/POST with object {"name": "test"} =
/api/database/test is the result
Anyone knows how to avoid that behaviour?
Greetings Kern
My View:
window.databaseView = Backbone.View.extend({
el: '#content',
template: new EJS({url: 'js/templates/databaseView.ejs'}),
initialize: function() {
var self = this;
this.collection.fetch({
success: function() {
console.log(self.collection);
var test = self.collection.get("_system");
console.log(test);
self.collection.get("_system").destroy();
self.collection.create({name: "test"});
}
});
},
render: function(){
$(this.el).html(this.template.render({}));
return this;
}
});
Model:
window.Database = Backbone.Model.extend({
initialize: function () {
'use strict';
},
idAttribute: "name",
defaults: {
}
});
Collection:
window.ArangoDatabase = Backbone.Collection.extend({
model: window.Database,
url: function() {
return '../../_api/database/';
},
parse: function(response) {
return _.map(response.result, function(v) {
return {name:v};
});
},
initialize: function() {
this.fetch();
},
getDatabases: function() {
this.fetch();
return this.models;
},
dropDatabase: function() {
},
createDatabse: function() {
}
});
By default, Backbone create models URLs this way: {collection url}/{model id}.
It consider the collection URL to be a base URL in a RESTful way.
Here you only want to set the Model url property to the URL you whish to call. That'll overwrite the default behavior. http://backbonejs.org/#Model-url