I have a tastypie API and now I'm playing around with backbone.js . To get both play together nicely I use backbone-tastypie.
This works very well.
Now I want to add offline functionality by using backbone.offline. This are my models and resources in backbone.js:
var Pizza = Backbone.Model.extend({
urlRoot: '/api/v1/pizza/',
});
var Topping = Backbone.Model.extend({
urlRoot: '/api/v1/topping/'
});
var PizzaCollection = Backbone.Collection.extend({
model: Pizza,
url: '/api/v1/pizza/',
initialize: function() {
this.storage = new Offline.Storage('pizza', this);
}
});
var ToppingCollection = Backbone.Collection.extend({
model: Topping,
url: '/api/v1/topping/',
initialize: function() {
this.storage = new Offline.Storage('topping', this);
}
});
Then if I create a collection and do a incremental sync on the stoage object, the request against the API loads normally, but I still have no models in the collection:
var pizzas = new PizzaCollection();
pizzas.storage.sync.incremental();
Can someone help me out here with knowledge about putting together backbone-tastypie and backbone.offline?
Related
I am creating a single page app that lets users filter for data upon two criteria (Skills and Location). This data is to be populated from two separate web services.
There is a model for each service to consume the data using REST style requests.
I want to use both bits of data in this one view. From my understanding a collection can hold multiple instances of one type of Model e.g. "Movie"
var Movies = Backbone.Collection.extend({
model: Movie,
initialize: function() {
console.log("");
console.log("Movie Collection initialize");
console.log(this);
console.log(this.length);
console.log(this.models);
}
});
var movie1 = new Movie({
"title": "Bag It",
"averageUserRating": 4.6,
"yearReleased": 2010,
"mpaaRating": "R"
});
var movie2 = new Movie({
"title": "Lost Boy: The Next Chapter",
"averageUserRating": 4.6,
"yearReleased": 2009,
"mpaaRating": "PG-13"
});
However I am trying to implement the pattern below, where the collection has two Models. Is this an anti pattern for Backbone. How should this be tackled?
define([
'underscore',
'backbone',
'models/locationsModel',
'models/skillsModel'
], function (_, Backbone, Location, Skills)
{
'use strict';
var FiltersCollection = Backbone.Collection.extend({
// The filters collection requires these two models that will provide data to the filters view
location: new Location(),
skills: new Skills(),
initialize: function() {
//Do stuff
}
});
return new FiltersCollection();
});
I can't advise on what is best for you because I can't visualise your data properly based on the info provided. But if you observe the collection constructor in the Backbone source:
if (options.model) this.model = options.model;
Then in _prepareModel:
var model = new this.model(attrs, options);
And we knew that "model" is a function anyway, and a function can return what you want. So providing your two different data sources have some attribute that can identify them you can do something like this:
var SkillModel = Backbone.Model.extend({
sayMyName: function() {
return 'I am a skill model and I am skilled at ' + this.get('name');
}
});
var LocationModel = Backbone.Model.extend({
sayMyName: function() {
return 'I am a location model and I am relaxing in ' + this.get('name');
}
});
function FilterModel(attrs, options) {
if (attrs.type === 'skill') {
return new SkillModel(attrs, options);
} else if (attrs.type === 'location') {
return new LocationModel(attrs, options);
}
}
var FilterCollection = Backbone.Collection.extend({
model: FilterModel
});
var filteredCollection = new FilterCollection([{
type: 'skill',
name: 'carpentry'
}, {
type: 'location',
name: 'India'
}, {
type: 'skill',
name: 'plumbing'
}]);
var outputEl = document.querySelector('#output');
filteredCollection.each(function(model) {
outputEl.innerHTML += '<p>' + model.sayMyName() + '<p>';
});
<script src="http://underscorejs.org/underscore.js"></script>
<script src="http://backbonejs.org/backbone.js"></script>
<div id="output"></div>
I am a novice to Backbone.js. I am trying to create a UI where I have multiple tables.
There are 2 separate URLs that provide data in JSON format. 1st url gives the structure of the table,i.e., the column headers, the width, a corresponding dbfield name where the data in the table will come from.
The 2nd url gives the data for a table. This url takes an id as parameter that is available in the first url.
So for eg., there are 4 tables then the 1st url will give the structure details of all the 4 tables and the 2nd url will need to be called 4 times for the different id's for the tables and rendered.
Any suggestions on how to do this using Backbone.js. I have been able to work with the 1st url and create the 4 tables but need help on how to add the data from the 2nd url to the table by looping thru the 1st collection and calling the 2nd url.
Appreciate any assistance with this.
thanks.
Following is the backbone code I use to get the data from 1st url and pass it to my template to generate the html. One of the fields coming in this data is a parameter for the 2nd url.
var mModel = Backbone.Model.extend();
var Collection = Backbone.Collection.extend({
model: mModel,
url: 'http://xyz.com/sendjson',
initialize: function () {
this.deferred = this.fetch();
}
});
var View = Backbone.View.extend({
render: function () {
var collection = this.collection;
collection.deferred.done(function () {
var template = _.template($('#template').html(),
{
Collection: Collection
});
$('#main').html(template);
});
}
});
var myCollection = new Collection();
var myView = new View({
collection: myCollection
});
myView.render();
Ok here is what I came up with. It uses two separate collections. I tested this locally and it worked for me.
var mUrl = '/sendjson'
var iUrl = '/senditem'
var mCollection, iCollection
var MModel = Backbone.Model.extend({})
var IModel = Backbone.Model.extend({urlRoot: iUrl})
var IView = Backbone.View.extend({
template: '<%= mModel.color %><%= iModel.name %>',
render: function() {
var html = _.template(this.template, {
mModel: this.mModel.toJSON(),
iModel: this.iModel.toJSON()
})
this.$el.html(html)
return this
}
})
var MCollection = Backbone.Collection.extend({
model: MModel,
url: mUrl
});
var ICollection = Backbone.Collection.extend({
initialize: function(app, ids) {
var self = this
_.each(ids, function(id) {
var iModel = new IModel({ id: id })
self.add(iModel)
app.listenTo(iModel, 'sync', function() {
var view = app.mCollection.get(iModel.id).view
view.iModel = iModel
app.$el.append(view.render().$el)
});
iModel.fetch()
});
}
});
var App = Backbone.View.extend({
el: '#main',
initialize: function() {
this.mCollection = new MCollection()
var app = this
this.mCollection.on('sync', function () {
app.mCollection.each(function(mModel) {
var iview = new IView()
iview.mModel = mModel
iview.iModel = new IModel
mModel.view = iview
})
app.render()
var items = new ICollection(app,
app.mCollection.map(function(mModel) {
return mModel.get("parent").child1.child2.id;
});
this.mCollection.fetch();
},
render: function () {
var that = this
this.mCollection.each(function(mModel) {
that.$el.append(mModel.view.render().$el)
});
}
});
I am trying to understand the relationship between a model and a view. I've tried building a model and view to render that model.
I get the error Cannot call method 'toJSON' of undefined which I understand as the actual instance of the model is not being sent to the view.
I feel there is something missing in the initialize of the view?
The Model:
var sticky = Backbone.Model.extend({
defaults: {
title:"",
content:"",
created: new Date()
},
initialize: function() {
console.log("sticky created!");
}
});
The View:
var stickyView = Backbone.View.extend({
tagName:"div",
className:"sticky-container",
initialize: function() {
this.render();
console.log("stickyView created!");
},
render: function() {
$("#content-view").prepend(this.el);
var data = this.model.toJSON(); // Error: Cannot call method 'toJSON' of undefined
console.log(data);
var source = $("#sticky-template").html();
var template = Handlebars.compile(source);
$(this.el).html(template(data));
return this;
}
});
Creating new model and new instance of the view:
var Sticky = new sticky({title:"test"});
var StickyView = new stickyView();
You have to pass your model instance to your view, Backbone will do the rest:
constructor / initialize new View([options])
There are several special options that, if passed, will be attached directly to
the view: model, collection, el, id, className, tagName and
attributes.
which means you would create your view like this
var StickyView = new stickyView({model: Sticky});
And while you're at it, you could pass your compiled template and the DOM node you wish to set as your view element (and remove the tagName and className from your view definition) to avoid a strict coupling:
var stickyView = Backbone.View.extend({
initialize: function(opts) {
this.template = opts.template;
this.render();
console.log("stickyView created!");
},
render: function() {
var data = this.model.toJSON();
console.log(data);
this.$el.html(this.template(data));
return this;
}
});
var StickyView = new stickyView({
model: Sticky,
el: '#content-view',
template: Handlebars.compile($("#sticky-template").html())
});
I'm relatively new to Backbone and Underscore and have one of those questions that's not really an issue - just bugging me out of curiosity.
I built a very simple app that allows you to add and remove models within a collection and renders them in the browser. It also has the ability to console.log the collection (so I can see my collection).
Here's the weird thing: the ID's being generated are 1,3,5... and so on. Is there a reason specific to my code, or something to do with BB/US?
Here's a working Fiddle: http://jsfiddle.net/ptagp/
And the code:
App = (function(){
var AppModel = Backbone.Model.extend({
defaults: {
id: null,
item: null
}
});
var AppCollection = Backbone.Collection.extend({
model: AppModel
});
var AppView = Backbone.View.extend({
el: $('#app'),
newfield: $('#new-item'),
initialize: function(){
this.el = $(this.el);
},
events: {
'click #add-new': 'addItem',
'click .remove-item': 'removeItem',
'click #print-collection': 'printCollection'
},
template: $('#item-template').html(),
render: function(model){
var templ = _.template(this.template);
this.el.append(templ({
id: model.get('id'),
item: model.get('item')
}));
},
addItem: function(){
var NewModel = new AppModel({
id: _.uniqueId(),
item: this.newfield.val()
});
this.collection.add(NewModel);
this.render(NewModel);
},
removeItem: function(e){
var id = this.$(e.currentTarget).parent('div').data('id');
var model = this.collection.get(id);
this.collection.remove(model);
$(e.target).parent('div').remove();
},
printCollection: function(){
this.collection.each(function(model){
console.log(model.get('id')+': '+model.get('item'));
});
}
});
return {
start: function(){
new AppView({
collection: new AppCollection()
});
}
};
});
$(function(){ new App().start(); });
if you look in the backbone.js source code you'll notice that _.uniqueId is used to set a model's cid:
https://github.com/documentcloud/backbone/blob/master/backbone.js#L194
that means that every time you create a model instance, _.uniqueId() is invoked.
that's what causing it to increment twice.
Here is my Model View and Collection :
window.Report = Backbone.Model.extend({});
window.ReportCollection = Backbone.Collection.extend({
model: Report,
initialize: function(properties){
this.url = properties.url;
}
});
window.ReportCollectionView = Backbone.View.extend({
initialize: function(){
this.collection.reset();
this.render();
},
render: function(){
var self = this;
this.collection.fetch({
success: function(){
self.collection.each(function(model){
//pass model to subview
});
}
}
});
}
});
in the other part of the code I use the instantiate the above objects
var reportCollection = new ReportCollection({url:someURL});
var reportCollectionView = new ReportCollectionView({collection:reportCollection});
'someURL' is a REST based URL that returns JSON list of Objects
So far everything looks good. What I am trying to achieve is:
I must be able to refresh the 'reportCollection' by changing the url and this should trigger an updated 'reportCollectionView'. Thanks for any pointers
I suppose you could add a method to your collection which changes url and forces a fetch:
window.ReportCollection = Backbone.Collection.extend({
//...
changeUrl: function(url) {
this.url = url;
this.fetch();
}
});
and then bind to the "reset" event in your view:
window.ReportCollectionView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.collection.on('reset', this.render);
this.collection.reset();
},
//...
});
Then if you do this:
c = new ReportCollection(...);
v = new ReportCollectionView({ collection: c, ... });
You'll get your rendered view and then later you can:
c.changeUrl(...);
to set the new URL and that will trigger a render call on v.