Understanding backboneJS: View.extend() - javascript

Until today I thought using backbones functionality like
var PageView = Backbone.View.extend({
template: $.Deferred,
getTemplate: function(tplName) {
var self = this;
ajaxCall(tplName).then(function(hbs) {
self.template.resolve(hbs);
});
}
});
var home = new PageView();
home.getTemplate('home.hbs');
is similar to a pure JS OOP approach like
var PageView = function() {
this.template = $.Deferred();
}
PageView.prototype.getTemplate = function(tplName) {
var self = this;
ajaxCall(tplName).then(function(hbs) {
self.template.resolve(hbs);
});
}
var home = new PageView();
home.getTemplate('home.hbs');
However, I'm currently trying to rebuild a web-app with backbone and it seems like solving that deferred with
home.getTemplate('home.hbs');
will resolve this for all instances of the Backbone view PageView while in pure JS this would only resolve for that one particular instance home.
When I do a second instance:
var page1 = new PageView();
the template property is already resolved with the home.hbs template in backbone. Which is very weird to me.
So my guess is that I'm fundamentally misunderstanding how backbone views work.
Can someone enlighten me?

The difference is in the first snippet template property of all the instances refer to the same object and in the second snippet the property is set using a different/new instance of Deferred when the constructor function is executed. If you set the template property within the initialize function of the constructor you will get the same result as the second snippet:
var PageView = Backbone.View.extend({
// template: $.Deferred,
initialize: function() {
this.template = $.Deferred;
},
getTemplate: function(tplName) {
var self = this;
ajaxCall(tplName).then(function(hbs) {
self.template.resolve(hbs);
});
}
});
In the second example if you set the template to the prototype of the constructor then it will behave like the backbone constructor:
var PageView = function() {
// this.template = $.Deferred();
}
PageView.prototype.template = $.Deferred();
var instance1 = new PageView();
var instance2 = new PageView();
console.log( instance1.template === instance2.template ); // true

Related

Unable to modify ko observable when function defined with object prototype

I understand it is in general bad practice to modify prototypes with the object.prototype.whatever syntax when using knockout, but I'm trying to understand why this isn't working at all:
var Foo = function() {
var self = this;
self.bar = ko.observable("bar");
};
Foo.prototype.capitalizer = function() {
self.bar("Bar");
};
var vm = function() {
var self = this;
self.whatever = new Foo();
};
js fiddle here: http://jsfiddle.net/vvdo7z70/8/
when this works as expected:
var Foo = function() {
var self = this;
self.bar = ko.observable("bar");
self.capitalizer = function() {
self.bar("Bar");
}
};
var vm = function() {
var self = this;
self.whatever = new Foo();
};
js fiddle here: http://jsfiddle.net/vvdo7z70/10/
Is it just not possible to pass relevant ko bindings with the object.prototype syntax? or is there another way to do it?
For one, self isn't defined in Foo.prototype.capitalizer. Once that's fixed, you need to note that the binding click: whatever.capitalizer is using the function, not the method, which is to say, the whatever context is not provided. Instead, vm is provided as the context. This will work:
Foo.prototype.capitalizer = function () {
this.whatever.bar("Bar");
};
or
click: whatever.capitalizer.bind(whatever)

Knockout multiple viewmodels with same name variables conflict?

I bound multiple ko viewmodels to different panels in the same page, but when the viewmodels have properties with the same name they seem to lose their binding to their own viewModel like:
var Panel1ViewModel = function Panel1ViewModel() {
var self = this;
self.isVisible = ko.observable(false);
self.change1 = function() {
self.isVisible(!self.isVisible());
};
};
ko.applyBindings(Panel1ViewModel(), document.getElementById('panel1'));
var Panel2ViewModel = function Panel1ViewModel() {
var self = this;
self.isVisible = ko.observable(false);
self.change2 = function() {
self.isVisible(!self.isVisible());
};
};
ko.applyBindings(Panel2ViewModel(), document.getElementById('panel2'));
To make it more clear I recreated the problem in jsfiddle.
I know I can nest ViewModels with with but the page is big and some content is loaded dynamically so I want to separate it.
Can someone explain me why this is happening and wat a possible solution is?
You're not initiating your view models correctly. Try it like this:
var Panel1ViewModel = function Panel1ViewModel() {
var self = this;
self.isVisible = ko.observable(false);
self.change1 = function() {
self.isVisible(!self.isVisible());
};
};
ko.applyBindings(new Panel1ViewModel(), document.getElementById('panel1'));
var Panel2ViewModel = function Panel1ViewModel() {
var self = this;
self.isVisible = ko.observable(false);
self.change2 = function() {
self.isVisible(!self.isVisible());
};
};
ko.applyBindings(new Panel2ViewModel(), document.getElementById('panel2'));
http://jsfiddle.net/XWD96/3/
The difference is that the new operator will create a new object (this inside your view model). So by not having the new, this will point to the window in both view models, therefor causing conflicts.
You can read more about Constructor Functions (new) here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Using_a_constructor_function)

Backbone view that render child views of the same type causes endless loop

I have a category model that has child category models (That works fine) via this code:
var ImageSetCategory = Backbone.Model.extend({
childrenCategories : new Array(),
initialize: function () {
var self = this;
if (this.has('childrenCategories')) {
$.each(this.get('childrenCategories'), function () {
var category = new ImageSetCategory(this);
self.childrenCategories.push(category);
});
}
}
});
I also have a view that uses this model and renders all the children categories. (basicly, I'm attempting to make a tree view) It loops through the child categories using jquery, instantiates a new version of its self with each child category as the model, and renders it. But I'm hitting an endless loop that constantly is trying to process the same model.
var ImageSetCategoryView = Backbone.View.extend({
tagName: 'li',
className: 'nested-category',
template: Handlebars.templates.imageSetCategoryView,
render: function() {
var self = this;
var templateHtml = this.template(this.model.toJSON());
self.$el.html(templateHtml);
// *****************************
// ENDLESS LOOP
// this is always the same model from the array
// *****************************
$.each(self.model.childrenCategories, function () {
var categoryView = new ImageSetCategoryView({ model: this });
self.$el.children('ul').append(categoryView.render().el);
});
return this;
},
});
Why is this causing an endless loop? Am I'm not following best practices? My background is C# so I'm trying to accomplish this in an OOP way.
The reason is that all instances of ImageSetCategory share the same childrenCategories array. This way in ImageSetCategory.initialize function you create circular references (ImageSetCategory.childrenCategories points to the array and ImageSetCategory.childrenCategories[0] points to ImageSetCategory itself). This makes $.each in ImageSetCategoryView.render iterate over the same model. To avoid it you should initialize array inside of ImageSetCategory.initialize function:
var ImageSetCategory = Backbone.Model.extend({
initialize: function () {
var self = this;
this.childrenCategories = [];
if (this.has('childrenCategories')) {
$.each(this.get('childrenCategories'), function () {
var category = new ImageSetCategory(this);
self.childrenCategories.push(category);
});
}
}
});
To learn more about why this happens read about prototypes in JavaScript and how they are used to implement object-oriented paradigm.

Setting Backbone attributes in a model in a nested collection

Pretty new to Backbone JS and I need to know the 'right' way of looping through and setting attributes on models in a collection that is within a model.
My models look like this:
var mediaItem = Backbone.Model.extend({
});
var mediaItems = Backbone.Collection.extend({
model: mediaItem
});
var story = Backbone.Model.extend({
initialize: function () {
this.MediaItems = new mediaItems(this.get('MediaItems'));
this.MediaItems.parent = this;
}
});
What I want to do is loop through the MediaItems in a given story and set the width and height of each. If I do it like this...
storyInstance.MediaItems.each(function (mediaItem) {
mediaItem.set('Width', 200);
mediaItem.set('Height', 100);
});
...then the MediaItem models within the storyInstance.MediaItems property are correctly updated, but the objects within storyInstance.attributes.MediaItems are not. And it's the attributes tree that appears to be used when I subsequently call toJSON() on the Story model.
I can probably amend the above to loop through attributes instead, but I get the feeling I've set up the models wrong or there's a more standard way of doing this?
Thanks.
Probably initialize something other than what you expected.
The below code
var story = Backbone.Model.extend({
initialize: function () {
this.MediaItems = new mediaItems(this.get('MediaItems'));
this.MediaItems.parent = this;
}
});
should have been
var story = Backbone.Model.extend({
initialize: function () {
this.MediaItems = this.get('MediaItems');
this.MediaItems.parent = this;
}
});
and instantiating items should be done with instantiation of story model like
var storyInstance = new story({
MediaItems: new mediaItems()
})
then
story.MediaItems.each(function (mediaItem) {
mediaItem.set('Width', 200);
mediaItem.set('Height', 100);
});
would result updating both
Edit: Did not realize this was from '13. It showed up in questions tagged backbone.js and I did not notice the date/time till now.
Try to check for the instance of Array in the initialize section.
var story = Backbone.Model.extend({
initialize: function () {
if(this.get('MediaItems') instanceof Array){
this.MediaItems = new mediaItems(this.get('MediaItems'));
}
else {
this.MediaItems = this.get('MediaItems');
}
this.MediaItems.parent = this;
}
});

Call different functions of different views of backbone.js from a global function [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Call methods of different views in Backbone.js
I am new to backbone.js and I am using backbone.js with ASP.NET MVC 4.
I have a global class called SomeObject in which I have a deleteUser function. This function is binded with one of the click event of a button present in MyView2.
How do I call the following backbone.js different functions present in different views, from this global function/class.
call myMethodB of MyView 2
call myMethodA of MyView 1
call myMethodC of AppView
Please guide me on this. I am still learning backbone.js and might be doing something wrong. Thanks
var SomeObject = function (Id, Name) {
var self = this;
this.Id = Id;
this.Name = Name;
this.deleteUser = function () {
console.log(self.Id, self.Name);
// call myMethodB of MyView 2
// call myMethodA of MyView 1
// call myMethodC of AppView
};
};
var MyModel = Backbone.Model.extends({
});
// View for a Main Grid
var MyView1 = Backbone.View.extend({
...
myMethodA: function(){
// do something with View 1
}
...
});
// View for subgrid in Main Grid
var MyView2 = Backbone.View.extend({
...
myMethodB: function(){
// do something with View 2
}
...
});
var AppView = Backbone.View.extend({
...
myMethodC: function(){
// do something with App View
}
...
});
You should use Backbone events - http://backbonejs.org/#Events
var SomeObject = function (Id, Name) {
var self = this;
this.Id = Id;
this.Name = Name;
this.deleteUser = function () {
console.log(self.Id, self.Name);
viewInstances.MyView2.trigger('eventFormyMethodB')
viewInstances.MyView1.trigger('eventFormyMethodA')
viewInstances.AppView.trigger('eventFormyMethodC')
};
};
var viewInstances = {
MyView2: new MyView2({...}),
MyView1: new MyView1({...}),
AppView: new AppView({...})
}
For example
var AppView = Backbone.View.extend({
...
events: {
'eventFormyMethodC': 'myMethodC'
},
myMethodC: function(){
// do something with App View
}
...
});

Categories

Resources