How to pass a urlRoot to a model in Backbone - backbone.js

I need to pass a urlRoot to a model at runtime, as several different classes of the model use different urlRoots.
Here is my model:
App.Models.Table = Backbone.Model.extend({ });
And here is where I am going to use it:
var m = new App.Models.Table();
var t = new App.Collections.Tables(m, { url: this.url });
var tables = new App.Views.Tables({ collection: t, template: this.template });
this.url returns the correct value, based on the event that calls it. Am I passing my model into the collection wrong? Here is my collection:
App.Collections.Tables = Backbone.Collection.extend({
url: this.url,
model: App.Models.Table,
initialize: function(models, options) {
if (options && options.url) {
this.url = options.url;
}
this.fetch({
success: function(data, options) {
}
});
}
});
How do I pass in this.url to my model?

Assuming this.url is the correct url in your example, then do the following:
table = new App.Models.Table({
id: id
});
table.urlRoot = this.url;

The URL should be either a string constant or a function that returns a string. In your collection, you'd want to do something like:
App.Collections.Tables = Backbone.Collection.extend({
url: function() { return "http://my.url/" },
// or, url: "http://my.url"
});
Using an anonymous function gives you the ability to process some data (that is, potentially modify the string) before the request is sent out.
Am I understanding your question correctly?

Related

Backbone model is plain text file. How to fetch?

I have a model File which contains a plain text file. For example Github Gists have this url structure: https://gist.githubusercontent.com/140bytes/962807/raw/dfb480a5038214d152544bddb412dfe2e8656413/LICENSE.txt.
To do this, should I override fetch/save/etc, or should I override the model's sync?
var File = Backbone.Model.extend({
path: '',
contents: '',
initialize: function(options) {
this.path = options.path || '';
},
fetch: function() {
// Do I override fetch/save/etc?
$.get(this.path).done(function(contents) {this.contents = contents});
},
sync: function (method, model, options, error) {
// Or do I override sync?
}
});
You can just override parse, fetch and url method a little:
var File = Backbone.Model.extend({
url: function(){
return this.get('path')
},
// call original Backbone.Model#fetch with `dataType` equal `text` for $.ajax
fetch: function(options){
options = _.extend(options || {}, {
dataType: 'text'
});
this.constructor.__super__.fetch.call(this, options);
},
// store response in content attribute
parse: function(response){
return {content: response};
}
});
In this case your code will be more idiomatic and you will have all benefits of Backbone native methods (success and error callbacks to fetch, request and sync events, change events etc). You can use it like:
var someFile = new File({
path: 'http:/example.com/someFile.txt'
});
someFile.fetch({
success: function(){
console.log(someFile.get('content'); // => content of someFile.txt
}
});

Backbone.js Table - Separate URLs for table structure and underlying data

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)
});
}
});

Backbone.js dynamic type model similar to T type in java

I am updating to my earlier question based on the comment to get better understanding:
//Model's
Student = Backbone.Model.extend({
defaults: {
name: ''
}
});
Students = Backbone.Collection.extend({
model: Student,
initialize: function(){
},
parse: function(resp) {
return resp;
}
});
Teacher = Backbone.Model.extend({
defaults : {
name : '',
salary : ''
}
});
Teachers = Backbone.Collection.extend({
model: Teacher,
initialize: function(){
},
parse: function(resp) {
return resp;
}
});
/**
* I Just need WrapperModel only single class instead of two so that when ever I
* make collection of this class I can dynamically bind the 'data' attribute to
* either Teachers or Students
*/
WrapperModelStudent = Backbone.Model.extend({
defaults : {
message : '',
processingStatus : ''
},
initialize : function() {
this.data = new Students([new Student({name: "Marry"}),
new Student({name: "Susan"}),
new Student({name: "Samanta"})
]);
}
});
WrapperModelTeacher = Backbone.Model.extend({
defaults : {
message : '',
processingStatus : ''
},
initialize : function() {
this.data = new Teachers();
}
});
WrapperModelStudents = Backbone.Collection.extend({
model: WrapperModelStudent,
initialize: function(){
},
parse: function(resp) {
return resp;
}
});
WrapperModelTeachers = Backbone.Collection.extend({
model: WrapperModelTeacher,
initialize: function(){
},
parse: function(resp) {
return resp;
}
});
/**
*Trying below
*/
/***
* instead of using above two need to just use below one. How to do this??
*/
WrapperModel = Backbone.Model.extend({
defaults : {
message : '',
processingStatus : ''
},
initialize : function(obj) {
this.data = obj;
}
});
WrapperModelStudentsA = Backbone.Collection.extend({
model: WrapperModel(new Students()),
initialize: function(){
},
parse: function(resp) {
return resp;
}
});
WrapperModelTeachersB = Backbone.Collection.extend({
model: WrapperModel(new Teachers()),
initialize: function(){
},
parse: function(resp) {
return resp;
}
});
wrapperModelStudent = new WrapperModelStudent({message:"success",processingStatus:"y"});
wrapperModelStudents = new WrapperModelStudents([wrapperModelStudent]);
wrapperModelTeacher = new WrapperModelTeacher({message:"success",processingStatus:"y"});
wrapperModelTeachers = new WrapperModelTeachers([wrapperModelTeacher]);
wrapperModel = new WrapperModel({message:"success",processingStatus:"y"});
wrapperModelStudentsA = new WrapperModelStudentsA([wrapperModel]);
wrapperModelTeachersA = new WrapperModelTeachersA([wrapperModel]);
console.log(wrapperModelStudents);
console.log(wrapperModelTeachers);
I am getting following error for wrapperModel and I am not able to create it's object.
Uncaught TypeError: Object [object global] has no method 'set'
*************** backbone-min.js:10
g.Model
*************** backbone-min.js:10
d
*************** backbone-min.js:38
(anonymous function) sample.html:110
So is it possible to do in backbone.js??
I think you're overthinking it. The model attribute of Backbone Collection are not THAT useful. You basically won't need them if you're not fetching anything. If you are though, I think you missed something in Backbone's doc:
"A collection can also contain polymorphic models by overriding this property with a function that returns a model."
Oh, and your error comes from thoses lines:
model: WrapperModel(new Students()),
Now, why doesn't this work is pretty crystal clear but requires some knowledge about JavaScript objects//classes: using a class as a function WONT create an object, therefore your context IS NOT the class NOR an instance of it. In this case, the this will be the global object as your error reads, and Backbone needs an object context to work correctly.
You could also try new WrapperModel(new Students()). You'd have no error, but this wouldn't work. The model attribute expects a class.
tl;dr: your line of code doesn't make any sense.

Backbone.js reinstantiating collection does not update corresponding view

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.

backbone.js Newbie Collection

I am trying to write some backbone.js stuff to get a better understanding on where and if it fits in better for me on projects. Any way I have a site and I am loading a collection with page content.
Json data comes back with (pid,name,title,content) on my router the default is
defaultRoute: function (actions)
{
this.showInfo('food');
},
showInfo: function (id)
{
var view = new ContentView({ model: this._items.at(id) });
$(".active").removeClass("active");
$("#" + id).addClass("active");
view.render();
}
if I put a 0 in place of id in this "new ContentView({ model: this._items.at(0) })" I will get the first item in the collection and if I do this in the View:
var ContentView = Backbone.View.extend({
el: $('#content'),
render: function ()
{
this.el.empty();
$(this.el).append(this.model.attributes.content);
return this;
}
});
I get the content displayed perfectly but of course may not be the content I wanted
Is it possible to select from a collection based on name == "food"?? I dont want to have to map the content to id numbers defeats the purpose of storing in a db
Sorry if this seems like a foolish question but I have crawled all over looking and Im sure Im missing something simple
here is my full NavigationRouter code in case it helps
var NavigationRouter = Backbone.Router.extend({
_data: null,
_items: null,
_view: null,
routes: {
"p/:id": "showInfo",
"*actions": "defaultRoute"
},
initialize: function (options)
{
var _this = this;
$.ajax({
url: "page_data.php",
dataType: 'json',
data: {},
async: false,
success: function (data)
{
_this._data = data;
_this._items = new ItemCollection(data);
_this._view.render();
Backbone.history.loadUrl();
}
});
return this;
},
defaultRoute: function (actions)
{
this.showInfo('home');
},
showInfo: function (id)
{
var view = new ContentView({ model: this._items.at(id) });
$(".active").removeClass("active");
$("#l_" + id).parent().addClass("active");
view.render();
}
});
Backbone mixes in a bunch of Underscore's functions into its Collections.
So if you want to find the model in the collection where name === 'food', you can do:
var foodModel = this._items.find(function(model) {
return model.get('name') === 'food';
});
// this will set foodModel to the first model whose name is 'food'
As a side note, you don't need to call empty in your render function, which can just be:
render: function() {
$(this.el).html(this.model.get('content'));
return this;
}
jQuery's html function just replaces the content of an element with the html string you pass in.

Resources