Is it possible to use global variable in backbone.js view ?
exmpl:
var TodoView = Backbone.View.extend({
counter: null ; // is this possible ?
initialize: function(){
this.render();
},
render: function(){
}
});
You can easily store variables in at least 3 different scopes:
Regular instance variables unique to each view instance
these can be stored as properties on the view's this object
Example: this.selectedItem = this.$el.find('.selected');
Class-level variables shared by all instances of the view
these can be stored on the View class's constructor function
Example: TodoView.counter = 0; (put that after your entire .extend invocation)
True global variables (yes, these are bad, but since you asked)
you can leak these by omitting var or just explicitly assign to the window object
Example: window.appLaunchTime = new Date()
In addition to what Peter already said, if you are interested in having what amounts to a private variable that is available across all instances of the TodoView you create then you could do something like the following.
(function () {
var counter = 0; //This can be used now as a private variable inside TodoView
window.TodoView = Backbone.View.extend({
initialize: function(){
this.render();
counter += 1;
},
render: function(){
}
});
})();
try putting it in static scope
var TodoView = Backbone.View.extend({
TodoView.counter++//reference it this way
},{
counter: null ;})
may serve somewhat like a global variable
Related
I'm trying to reach a collection instance with the this keyword, when iterating through its models. Here is my test code:
myModel = Backbone.Model.extend({
url: 'model.com'
});
myCollection = Backbone.Collection.extend({
model: myModel,
url: 'collection.com',
iterateAll: function(){
this.each(function(item){
console.log(this.url);
console.log(item.url);
});
}
});
mod = new myModel();
col = new myCollection(mod);
col.iterateAll();
This code outputs:
undefined
model.com
How can I reference the collection correctly when using this.url? I want the output to read collection.com not undefined.
this is pretty weird in JavaScript.
iterateAll: function() {
var self = this; // _this is also common
this.each(function(item) {
console.log(self.url);
console.log(item.url);
});
}
Basically, JavaScript doesn't formally distinguish between methods and functions. As a result, every function invocation -- even simple anonymous callbacks like these -- gets its own this value. But this very frequently isn't...what anyone wants ever. So you can hold onto the "useful" value of this in a temporary variable and reference it in the callback.
This is so common that it's a feature of CoffeeScript via the => syntax, and it's going to be a feature of ES6. Until then...temp variables are we've got.
Do this instead:
...
iterateAll: function(){
var me = this;
me.each(function(item){
console.log(me.url);
console.log(item.url);
});
}
...
More info on this scope. http://javascriptplayground.com/blog/2012/04/javascript-variable-scope-this/
Is it possible for mustache lambda function to access its view instance this?
Backbone.View.extend ({
initialize: function (options) {
this.collection = options.collection // large backbone.collection
},
parseContent: function (){
return function (id, render){
//this.collection is undefined below
return this.collection.get (render (id)).get ('stuff);
}
}
});
Tried _.bind (this.parseContent, this) inside initialize (), this still carry the model context inside parseContent ().
My current workaround is saving this.collection to my app root namespace and access from there. Wondering is there a cleaner way to do this as intended above?
Appreciate your advice.
If you're going to pass around the function returned by parseContent, you should
bind that function before returning it with _.bind,
and use _.bindAll in initialize to force this in parseContent on each instance.
Your view could be written as
Backbone.View.extend ({
initialize: function (options) {
_.bindAll(this, 'parseContent');
// you don't need this.collection = options.collection
// collection is part of the special variables handled By Backbone
},
parseContent: function (){
var f = function (id, render){
console.log(this.collection);
}
return _.bind(f, this);
}
});
And a demo http://jsfiddle.net/nikoshr/VNeR8/
I'm clearly missing the obvious here, but it's been a long day already.
The following code creates an infinite loop in the browser:
M = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("ModelName"),
initialize: function() {
this.on("change", this.save, this);
}
});
While the following code works fine:
M = Backbone.Model.extend({
localStorage: new Backbone.LocalStorage("ModelName"),
initialize: function() {
this.on("change", this.modelChanged, this);
},
modelChanged: function() {
this.save();
}
});
What's the difference?
(Yes, I'm using local storage for a model rather than a collection, but the model is a singleton that doesn't exist in a collection.)
The change event passes arguments to its handler, and if save is called with arguments, it applies them as new attributes to the model, and causes a change event (which passes attributes to save... which causes a change... etc)
Background:
I am making changes to an application that uses backbone.js with Handlebars as the templating engine. After a change event fires I need to create html that is appended to the current DOM structure which is basically just a spit-out of information that is contained in the model. This change needed to fit in the already established application structure.
Issue:
I have created a new view that uses a Handlebars template and the model to create the html. I then instantiate that view and call the render function and append the output using JQuery. What I am noticing is that when the html is rendered the model that is passed in because attributes on the $el instead of filling in the template (like I think it should).
View I'm altering:
$.hart.TestView = Backbone.View.extend({
tagName: "li",
template: Handlebars.compile($('#templateOne').html()),
initialize: function () {
this.model.on('change', function () {
this.createMoreInfoHtml();
}, this);
},
selectSomething: function () {
this.$el.removeClass('policies');
this.createMoreInfoHtml(); //function created for new view stuff
},
createMoreInfoHtml: function () {
var id = this.$el.attr('data-id', this.model.get("ID"));
$('.info').each(function () {
if ($(this).parent().attr('data-id') == id
$(this).remove();
});
var view = new $.hart.NewView(this.model, Handlebars.compile($("#NewTemplate").html()));
$('h1', this.$el).after(view.render().el);
},
render: function () {
... //render logic
}
});
View I Created:
$.hart.NewView = Backbone.View.extend({
initialize: function (model, template) {
this.model = model;
this.template = template;
},
render: function () {
this.$el.html(this.template({ info: this.model }));
this.$el.addClass('.info');
return this;
}
});
Json the is the model:
{
"PetName":"Asdfasdf",
"DateOfBirth":"3/11/2011 12:00:00 AM",
"IsSpayNeutered":false,
"Sex":"F",
"SpeciesID":2,
"ID":"ac8a42d2-7fa7-e211-8ef8-000c2964b571"
}
The template
<script id="NewTemplate" type="text/html">
<span>Pet Name: </span>
<span>{{this.PetName}}</span>
</script>
So now to the question: What am I doing wrong? Why are the properties of the model being created as attributes on the $el instead of filling in the template? Can someone please direct me as to how to get the results I am looking for?
Let's skip the problem Jack noticed.
The way you're creating your view is just wrong. It may work as you get the expected arguments in the initialize function, but it has unexpected behaviors you don't see. See the View's constructor:
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
this._configure(options || {});
Now let's have a look at this _configure method:
_configure: function(options) {
if (this.options) options = _.extend({}, _.result(this, 'options'), options);
_.extend(this, _.pick(options, viewOptions));
And of course...
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
Ok here we are... Basically when passing the model as the options argument, you're passing an object with an attributes key (the attributes of your model). But this attributes key is also used in the View to bind attributes to its element! Therefore the behavior your noticed.
Now, other wrong thing. You're compiling your template each time you create a new function, but not using it as a singleton either. Put your template in the view:
$.hart.NewView = Backbone.View.extend({
template: Handlebars.compile($("#NewTemplate").html(),
And change your view's creation to make the whole thing work:
new $.hart.NewView({model: this.model});
Oh, and get rid of this useless initialize method. You're just doing things Backbone already does.
I've been using backbone for quite some time now, and each time I get dynamic lists of views that have their own events and behaviors, I wonder how should they be stored. I've two ways and my thoughts on them are..
Store views internally in another view. This requires overhead in proper filtering, but is sort-of independent from DOM + might have better memory usage
Just generate views, put them in DOM and trigger events of views with jquery, like $('#someviewid').trigger('somecustomfunction'); - easier to write and access but dependencies are harder to see and I'm not certain that view/model gets deleted if I just remove DOM node
What would you recommend?
So here is expanded second example, where new views are just appended to internal html and storyViews themselves are forgotten. But If I want to access specific view from this list, I would have to use DOM attributes, like id or data and then trigger view functions with jquery accessors
Devclub.Views.StoriesList = Backbone.View.extend({
initialize: function () {
this.collection.bind('reset', this.reset, this);
this.collection.fetch();
},
reset: function (modelList) {
$(this.el).html('');
var me = this;
$.each(modelList.models, function (i, model) {
me.add(model);
});
},
add: function (model) {
var contact_model = new Devclub.Models.Story(model);
var view = new Devclub.Views.Story({
model: contact_model
});
var storyView = view.render().el;
$(this.el).append(storyView);
}
});
In contrast, I could instead store same view list in an array and iterate over it if I want to call some view methods directly
I think you should keep a reference of the child views. Here is an example from the book by Addy Osmani.
Backbone.View.prototype.close = function() {
if (this.onClose) {
this.onClose();
}
this.remove(); };
NewView = Backbone.View.extend({
initialize: function() {
this.childViews = [];
},
renderChildren: function(item) {
var itemView = new NewChildView({ model: item });
$(this.el).prepend(itemView.render());
this.childViews.push(itemView);
},
onClose: function() {
_(this.childViews).each(function(view) {
view.close();
});
} });
NewChildView = Backbone.View.extend({
tagName: 'li',
render: function() {
} });