Reputation: 1619
I'm having trouble in attempting to use two models with the same view in backbone.js. Somehow the view is being rendered before the triggers are getting triggered to begin the render function.
Here are the models:
APP.Models.FamilyPreferences = Backbone.Model.extend({
initialize: function( attributes, options ) {
_.bindAll(this, 'success_handler', 'populate');
this.url = options.url;
},
populate: function(){
this.fetch({success: this.success_handler});
},
success_handler: function(){
this.trigger("change");
}
});
APP.Models.Preferences = Backbone.Model.extend({
initialize: function( attributes, options ) {
_.bindAll(this, 'success_handler', 'error_handler', 'populate');
this.url = options.url;
},
populate: function(){
this.fetch({success: this.success_handler, error:this.error_handler});
},
success_handler: function(){
this.exists = true;
this.trigger("change");
},
error_handler: function(){
this.exists = false;
this.trigger("change");
}
});
Here is the relevant code from the view:
APP.Views.PreferencesFormView = Backbone.View.extend({
templates: [{name:"preferences_template", file_path:"preferences_form.html"}],
initialize: function(options){
_.bindAll(this, 'render', 'renderPrereq');
var family_url = "services/family/" + options.family_id;
var preferences_url = "services/preferences/familyID/" + options.family_id;
var ctx = this;
this.alreadyRendered = false;
this.modelsCurrentlyLoaded = [];
this.models = {};
this.models.family = new APP.Models.FamilyPreferences({}, {url: family_url});
this.models.family.on('change', function(){ctx.renderPrereq("family");});
this.models.family.populate();
this.models.preferences = new APP.Models.Preferences({}, {url:preferences_url});
this.models.preferences.on('change', function() {ctx.renderPrereq("preferences");});
this.models.preferences.populate();
},
renderPrereq: function(newmodel){
var inside = ($.inArray(newmodel, this.modelsCurrentlyLoaded)>-1) ? true : false;
if (!(inside)){this.modelsCurrentlyLoaded.push(newmodel);}
var total = Object.keys(this.models).length;
if(this.modelsCurrentlyLoaded.length == total && !this.alreadyRendered){
this.alreadyRendered = true;
this.render();
}
},
render: function(){
var family_data = {
id: this.models.family.attributes.id,
familyGroup: this.models.family.attributes.groupNum,
familyId: this.models.family.attributes.familyId,
probandDob: this.models.family.attributes.childDob,
}
var preferences_data = {
mother: this.models.preferences.attributes[0],
father: this.models.preferences.attributes[1],
exists: this.models.preferences.exists
}
this.$el.html(this.compiledTemplates.preferences_template(family_data));
//bunch of javascript making the page work
}
});
The template are getting loaded through another js function elsewhere that to the best of my knowledge is working correctly. Somehow the render function is getting called before the success handlers. I can't figure out how. The only side effect it has is that the Preferences model doesn't get the exists property and is hence undefined which cause all sorts of problems. Also, options.family_id is set correctly. Any help would be appreciated. Thank you in advance.
EDIT: It also seems to be going to renderPrereq up to six times which I also can't figure out.
JSON - Family model
{
"id": 1,
"familyId": "family01",
"collectionDate": "2013-01-01",
"childDob": "2001-05-06",
"groupNum": "Two",
"probands": [],
"mother": null,
"father": null,
"siblings": []
}
JSON - First part of Preferences Model
[{
"main": "illness-only",
"obesity": "No",
"bloodPressure": "No",
"diabetes": "No",
"heart": "No",
"alzheimers": "No",
"parkinsons": "No",
"mentalHealth": "No",
"breastOvarianCancer": "No",
"prostateTesticularCancer": "No",
"otherCancer": "No",
"childSickleCell": "No",
"childCysticFibrosis": "No",
"childMuscularDystrophy": "No",
"childAutism": "No"
},
{
"main":"more-questions",
"obesity":"No",
"bloodPressure":"Yes",
"diabetes":"No",
"heart":"Unsure",
"alzheimers":"No",
"parkinsons":"Yes",
"mentalHealth":"No",
"breastOvarianCancer":"No",
"prostateTesticularCancer":"No",
"otherCancer":"No",
"childSickleCell":"No",
"childCysticFibrosis":"No",
"childMuscularDystrophy":"No",
"childAutism":"No"}]
Headers
Accept: application / json, text / javascript, /; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache Connection:keep-alive
Cookie:csrftoken=bDIpdvAPBdWF6dZe9BkpsFSF4wiGl2qX
Host:localhost:8080 Pragma:no-cache Referer:localhost:8080/CSER / index.html
User - Agent: Mozilla / 5.0(Macintosh; Intel Mac OS X 10_8_3) AppleWebKit /
537.36(KHTML, like Gecko) Chrome / 27.0.1453.110 Safari / 537.36 X - Requested - With: XMLHttpRequest
Try this code ..
APP.Models.FamilyPreferences = Backbone.Model.extend({
initialize: function (attributes, options) {
_.bindAll(this, 'success_handler', 'populate');
this.url = options.url;
},
populate: function () {
this.fetch();
},
// This is not required
// It will automatically render it as we are listening to the sync event
success_handler: function () {
// Not required
// sync event will take care of it
}
});
APP.Models.Preferences = Backbone.Model.extend({
initialize: function (attributes, options) {
_.bindAll(this, 'success_handler', 'error_handler', 'populate');
this.url = options.url;
},
populate: function () {
this.fetch({
success: this.success_handler,
error: this.error_handler
});
},
success_handler: function () {
this.exists = true;
// Not required
// sync event will take care of it
},
error_handler: function () {
// Do something else
}
});
APP.Views.PreferencesFormView = Backbone.View.extend({
templates: [{
name: "preferences_template",
file_path: "preferences_form.html"
}],
initialize: function (options) {
_.bindAll(this, 'render', 'renderPrereq');
var family_url = "services/family/" + options.family_id;
var preferences_url = "services/preferences/familyID/" + options.family_id;
var ctx = this;
this.alreadyRendered = false;
this.modelsCurrentlyLoaded = [];
this.models = {};
// Family Model
this.models.family = new APP.Models.FamilyPreferences({}, {
url: family_url
});
this.listenTo( this.models.family, 'change', function () {
ctx.renderPrereq("family");
});
// This will take care of rendering it on Sync with server
// No need to triggereing the event explicitly..
this.listenTo( this.models.family, 'sync', function () {
ctx.renderPrereq("family");
});
this.models.family.populate();
// Family Preference Model
this.models.preferences = new APP.Models.Preferences({}, {
url: preferences_url
});
this.listenTo( this.models.preferences, 'change', function () {
ctx.renderPrereq("family");
});
// This will take care of rendering it on Sync with server
// No need to triggereing the event explicitly..
this.listenTo( this.models.preferences, 'sync', function () {
ctx.renderPrereq("preferences");
});
this.models.preferences.populate();
},
renderPrereq: function (newmodel) {
var inside = ($.inArray(newmodel, this.modelsCurrentlyLoaded) > -1) ? true : false;
if (!(inside)) {
this.modelsCurrentlyLoaded.push(newmodel);
}
var total = Object.keys(this.models).length;
if (this.modelsCurrentlyLoaded.length == total && !this.alreadyRendered) {
this.alreadyRendered = true;
this.render();
}
},
render: function () {
var family_data = this.models.family.toJSON());
var preferences_data = {
mother: this.models.preferences.attributes[0],
father: this.models.preferences.attributes[1],
exists: this.models.preferences.get('exists') ? this.models.preferences.get('exists') : false
}
this.$el.html(this.compiledTemplates.preferences_template(family_data);
//bunch of javascript making the page work
}
});
Upvotes: 2
Views: 549
Reputation: 55740
Looks like the problem is the way it is constructed... I don't think the render
is called before success_handler
.
If you look at the renderPrereq
method , 2 models on change will call this method on success.
But the fetch
methods are async. So you never know which handler will be called first.
And when the change
on the model is triggered. This calls the render
method which is inside the renderPrereq
method.
Upvotes: 1