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)
});
}
});
Related
When I render this view
var MyView = Backbone.View.extend({
/* el : '.myview', used when rendered on router request */
render : function () {
var data = new Data(); /* a collection from ajax request */
var that = this;
data.fetch({
success : function (bla, data) {
var template = _.template( $('#temp').html(), {data: data.players} );
that.$el.html(template);
}
});
}
});
on router request, it works:
var Router = Backbone.Router.extend({
routes : {
'bla' : 'bla'
}
});
var myView = new MyView();
var router = new Router();
router.on('route:bla', function () {
myView.render();
});
But when I want it simply to load with the page, it doesn't:
var myView = new MyView({ el: $(".myview") });
Because in the last line you posted, you missing call to render function.
var myView = new MyView({ el: $(".myview") });
myView.render();
Besides of that, I see the workflow of initialization a bit wiered. Namely, requesting the data inside .render() function, that violates view single responsibility. Something, that I do in my applications.
var Router = Backbone.Router.extend({
routes : {
'bla' : 'bla'
},
bla: function () {
app.BlaApp();
}
});
app.BlaApp = function () {
var collection = new Collection();
collection.fetch({
success: function (collection) {
var view = new app.BlaView({model: collection});
view.render();
}
})
}
app.BlaView = Backbone.View.extend({
render: function () {
var template = // render template based on this.model;
this.$el.html(template);
return this;
}
});
During the initialize function of my app I would like to default to my search page and pass my LeagueCollection as the model.
I am encountering an issue where I can add a watch to this.searchResults in my App initialize and see models: Array[3] as expected,
but when the this.model.toJSON() in the view is called I get the error object has no method toJSON.
This code was working fine with a in memory collection and then I switched to using backbone.localstorage.js to store the app data locally.
So my question is: why is the model not populated in the view?
In my main.js I have
var AppRouter = Backbone.Router.extend({
routes: {
"": "list",
...
},
initialize: function () {
this.searchResults = new LeagueCollection();
this.searchPage = new SearchPage({
model: this.searchResults.fetch()
});
this.searchPage.render();
},
...
});
In my Search Page view
window.SearchPage = Backbone.View.extend({
initialize:function () {
this.template = _.template(tpl.get('search-page'));
},
render:function (eventName) {
var self = this;
$(this.el).html(this.template(this.model.toJSON()));
this.listView = new LeagueListView({el: $('ul', this.el), model: this.model});
this.listView.render();
return this;
},
...
});
The method collection.fetch doesn't return the collection -- it's asynchronous. What you probably want is to use its success callback:
this.searchResults = new LeagueCollection();
var self = this;
this.searchResults.fetch({
success: function(collection, response) {
self.searchPage = new SearchPage( { model: collection } );
self.searchPage.render();
}
});
The situation I'm experiencing is that my fetch is working fine, I can see the data in my backbone collection, if I step into the method where I assign the item from the collection to the model var or pause for a second or two, all is well: the line of code this.member gets populated
this.member = this.members.get(1);
. If I just let the code run, I wind up passing in a null model to my view. I don't understand what I'm missing. Do I need to bind data in the collection before I access the collection? I'm trying to fall in love with Backbone, but so far, she's been a cruel mistress...
`
//create the namespace
var Endeavor = {
Models: {},
Collections: {},
Views: {},
Templates: {}
};
Endeavor.Models.Member = Backbone.Model.extend({
idAttribute: "Id"
});
Endeavor.Collections.Members = Backbone.Collection.extend({
model: Endeavor.Models.Member,
url: "Http://localhost:60000/api/members/" + "1", // $.cookie('UserId')
initialize: function () {
console.log("Members collections init");
}
});
Endeavor.Views.MemberView = Backbone.View.extend({
id: "memberForm",
template: "#memberTemplate",
initialize: function () {
console.log('init member view');
},
render: function () {
console.log('memberView render called');
console.log(this.model.toJSON());
var html = $(this.template).tmpl();
$(this.el).html(html);
$("#Name").text = this.model.Name;
}
});
jQuery(document).ready(function () {
// router
Endeavor.Router = Backbone.Router.extend({
routes: {
"": "lists",
},
lists: function () {
this.members = new Endeavor.Collections.Members();
this.members.fetch();
this.member = this.members.get(1);
var memberView = new Endeavor.Views.MemberView({ model: this.member });
memberView.render();
$("#content").html(memberView.el);
}
});
// populate local variables
var appRouter = new Endeavor.Router();
Backbone.history.start();
});
`.
Members.fetch is async call. Try to render view on members reset event, or pass success callback in fetch method, or call this.members.fetch({async: false}). There are many different options.
I've fetch() a model by a server and I want to render the results of fetch() with a view.
The results of fetch() is an array of objects (var risultati) and I want render this var risultati. I've tried but nothing works.
var AppRouter = Backbone.Router.extend({
routes: {
"": "list",
},
initialize: function () {},
list: function () {
var utente = new Person();
var risultati;
utente.fetch({
success: function (data) {
var ris = data.attributes;
var risultati = ris.results;
console.log(risultati); /* risultati contains array of object to render*/
}
});
this.page = new UserListView({
model: this.utente
});
$('body').append(this.page.$el);
}
});
You may be having problems because your call to render the view is occurring separate from your utente.fetch() call.
Since .fetch() is asynchronous, your view code will be executed before .fetch() has finished. You should add the view creation/rendering as part of the success function, or you should bind the change event that occurs when the model is updated to fire off a new function that contains your view creation.
You should separate your MVC logic... don't attach objects (collections I think) to a router route handler.
Assuming that you are trying to render a collection of person models, I suggest you use a model and view for the person and a collection and a view for handling the "array of objects" :
var Person = Backbone.Model.extend({
initialize : function(){
// initialize the view
this.view = new PersonView({model : this});
}
}),
PersonView = Backbonke.View.extend({
render : function(){
// render your person
}
}),
UserList = Backbone.Collection.extend({
model : Person,
initialize : function(){
this.view = new UserListView({
collection : this
});
},
update : function(){
var self = this;
this.fetch({
success: function (data) {
var ris = data.attributes;
var risultati = ris.results;
console.log(risultati); /* risultati contains array of object to render*/ self.view.render();
}
});
}
}),
UserListView = Backbone.View.extend({
render : function(){
this.collection.each(function(el,i){
el.view.render();
});
}
});
and then use it as :
var page = new UserList();
var AppRouter = Backbone.Router.extend({
routes: {
"": "list",
},
initialize: function () {},
list: function () {
page.update();
}
});
Hope this helps!
I have the following backbone.js code and i have a problem in that event before i fetch the "add" event is triggered from the collections. Adding this.field.add(list_fields); in the success: fetch has resulted in an error. How do i make sure the model is fetched then the add event is run after that
$(function() {
$(".chzn-select").chosen();
/*********************************Models*************************************************/
var Table = Backbone.Model.extend({
urlRoot : '/campusfeed/index.php/welcome/generate'
});
var Field = Backbone.Model.extend({
urlRoot: '/campusfeed/index.php/welcome/generate'
});
/**************************Collections*************************************************/
Tables = Backbone.Collection.extend({
//This is our Friends collection and holds our Friend models
initialize : function(models, options) {
this.bind("add", options.view.addFriendLi);
//Listen for new additions to the collection and call a view function if so
}
});
var Fields = Backbone.Collection.extend({
model:Field,
url:'http://localhost/campusfeed/index.php/welcome/generateFields',
initialize : function(models, options) {
this.bind("add", options.view.getFields);
}
});
/************************************Views**************************************************/
var m="shit";
var app = Backbone.View.extend({
el:'body',
initialize:function(model,options){
//Create collections in here
this.table = new Tables(null,{
view : this
});
this.field = new Fields(null,{
view : this
});
},
events : {
"click #choose" : "generate"
},
generate:function(){
var table = ( this.$("#table option:selected").text());
var dbname = ( this.$("#database").text());
var list_fields = new Field();
list_fields.urlRoot = list_fields.urlRoot+"/"+dbname+"/"+table;
list_fields.fetch({
success:function(){
console.log(JSON.stringify(list_fields));
}
});
this.field.add(list_fields);
},
getFields:function(model){
console.log(JSON.stringify(model));
}
});
var apprun = new app;
/* var data = new Fields();
data.url=data.url+"/some/data";
alert(data.url);
data.fetch();
var staff = new Table();
staff.fetch();
var field = new Field();*/
});
the problem is the context of "this". the success callback function has "this" set to the list_fields. you can work around this with a "self" or "that" variable:
generate:function(){
var table = ( this.$("#table option:selected").text());
var dbname = ( this.$("#database").text());
var list_fields = new Field();
list_fields.urlRoot = list_fields.urlRoot+"/"+dbname+"/"+table;
var that = this;
list_fields.fetch({
success:function(){
console.log(JSON.stringify(list_fields));
that.field.add(list_fields);
}
});
},
as a side note - your collections should never have a reference to a view. instead, your view should reference the collection and bind to the collection event
var Fields = Backbone.Collection.extend({
model:Field,
url:'http://localhost/campusfeed/index.php/welcome/generateFields',
});
var app = Backbone.View.extend({
initialize:function(model,options){
//Create collections in here
this.field = new Fields();
this.field.bind("add", this.getFields, this);
},
getFields: function(){ ... }
});