Why is el needed as view parameter - backbone.js

Since you can specify a view's el as one of it's properties why does it still need to be passed as a parameter to work?
Eg.
var DetailsView = Backbone.View.extend({
el: $(".details")
});
As parameter:
var DetailsView = new DetailsView({ el: $(".details") });

It doesn't need to be specified. If you already at that point have an element that matches the selector, this should work:
$('body').append($("<div class='details'></div>"));
var DetailsView = Backbone.View.extend({
el: $(".details")
});
console.log(new DetailsView().el.className); // details
I suspect that your element is not in DOM when you define the view.
How Backbone.View works is that it checks whether you specified el as an attribute and if not, it checks if el was specified in the prototype, and if not, it uses the tagName, className and attributes properties on the prototype to create a new element for the view.

Related

Marionette collection view does not update when model added

I am new to Marionette. So may be its pretty basic.
I have a collection initialized globally and is empty during creation.
App.Colors= Backbone.Collection.extend({
model : App.Color,
comparator : "price"
});
var appColors = new App.Colors();
If I create a Marionette collection view with this empty collection.
var colorview= new App.ColorView({collection:appColors});
and later on add to the collection appColors.
appColors.add({code:'red'})
Shouldn't the view be updated automatically since it listens to collection.add. I know it will work fine if I type:
colorview.collection.add({code:'red'})
In order for it to work, you must bind the view rendering to the collection add event, so basicly in your view declaration you must have something like this :
App.ColorView = Backbone.View.extend({
initialize: function() {
this.listenTo(this.collection, 'add', this.render);
...
}
...
});

Object doesn't support property or method 'bind' - Backbone.js

I have build my application in Backbone.js using MVC. Everything, is running fine in Chrome/Firefox/IE 9 and above but not in IE8 and below:-
var userDetailView = backbone.View.extend({
el: '#user-details',
tagName: 'div',
template: Handlebars.templates.UserDetails,
model: userModel,
initialize: function () {
_.bindAll(this, "render");
this.model.bind('change', this.render);
return this;
}
});
I am getting error as below:-
SCRIPT438: Object doesn't support property or method 'bind'
Can anyone help?
What is this.model? How are you instantiating that view? I'd guess that this.model is not what it should be. In a view, the model property should be a model instance and you're suppose to say things like:
var m = new Model;
var v = new View({ model: m });
A model instance will have a bind method (AKA on) but, in non-stone-age browser, so will a function:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
A model "class":
var M = Backbone.Model.extend({ ... });
is a function (just like anything else you can call new on in JavaScript). This means that you can say things like this in newer browsers:
var M = Backbone.Model.extend({ ... });
var v = new View({ model: M });
and this.model.bind('change', this.render) will execute inside the View, it won't call the bind you're looking for but it will call a bind, it will call Function.bind.
Start passing a model instance to your view and things should start making more sense.
Clarification: If you check the MDN Browser compatibility section on Function.bind, you'll see that there is no bind for functions in IE until IE9. So Chrome, Firefox, and IE9 all support calling bind on functions but IE8 does not; this precisely matches the observed behavior.

Creating backbone views with models from other views

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.

jquery $(this) not working with coffeescript and backbone

$(this) is not working with cofeescript and backbone.Its generating valid code but I don't understand why it was not working. For work around I used $(event.target).
Jobmatch.Views.Jobs ||= {}
class Jobmatch.Views.Jobs.JobView extends Backbone.View
template: JST["recommendation/templates/jobs/job"]
initialize: () ->
#ajs = new Jobmatch.Collections.ApplicantJobsCollection()
#ajs.reset(#options.applicant_jobs || [])
#aj = new #ajs.model()
#index = #options.index || 0
events:
"click .job_apply" : "apply"
tagName: "tr"
apply: (event)->
target = $(this) // As this is not working as I expected,So I used below line.
target = $(event.target)
if #options.user_jobmatch_valid
#ajs.create({job_id: #model.get('id') })
target.parents("a.job_apply").prev().click();
else
target.parents("a.job_apply").next().click();
false
render: ->
$(this.el).html(#template(#model.toJSON()))
#
And this cofeescript has generated following code:
JobView.prototype.apply = function(event) {
var target; target = $(this); // not working it is not elementt object
target = $(event.target);// this is element object ,working fine
target.parents("a.job_apply").prev().click();
};
In the backbone documentation, http://backbonejs.org/#View-delegateEvents
It states clearly:
All attached callbacks are bound to the view before being handed off
to jQuery, so when the callbacks are invoked, this continues to refer
to the view object.
This resolves one of the issues that many object-oriented programmers have with jQuery, that it overrides the context in all its event handlers. As stated earlier you can often use the event, which is passed as the handler's argument. However, event.target can often refer to child elements as opposed to this, which always refers to the element selected with the query. For instance:
<b>my link</b>
$('a').on('click', myfunction); function myfunction(event) {
this // refers to the "a" element
event.target // refers to the "b" element
}
In these cases, when attaching mouse events with Backbone to elements with children, use event.currentTarget to get a reference to the element that the event handler was attached to.
events: {
"click a" : "myFunction" },
...
function myFunction(event) {
var $this = $(event.currentTarget);
// Now, $this is the same as jQuery's $(this)
}
it all depends on the context,
this is in certain cases bound to your view itself,
take this example:
var myView = Backbone.View.extend({
events: { "click a" : "myfunction" },
initialize: function(){
_.bindAll(this, 'render', 'myfunction');
},
render: function(){
// rendering data here...
},
myfunction: function(e){
console.log(e.target); // will log the clicked DOM element => <a>
console.log(this); // will log the view => myView
}
});
window.v = new myView({ el : $('#mydiv') });
window.v.render();
as you can see, if you should run this example, you can see the differences between this and e.target. however, this is all due to the keyline in the initialize method.
_.bindAll(this, 'methodname', [ methodnames ] );
this binds the view to this when in 1 of the given methods.
if you would remove myfunction from that list, console.log(this); would log the element itself.
but, you would not be able to reach data or functions from your view....
you are free to chose if you bind the view in to this.
With events bound through a Backbone view, this will be set to the view itself. This is different from normal jQuery where this refers to the DOM element
So in a Backbone view:
events: { "click a" : "myfunction" },
...
function myfunction(e) {
this // refers to the view
e.target // refers to the element
}
Whereas in normal jQuery:
$('a').on('click', myfunction);
...
function myfunction() {
this // refers to the element
e.target // also refers to the element
}
In both cases, you can use e.target to refer to the element

Backbone.js el is not working

App.Views.VideoView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.model = this.options.model;
this.render();
},
render: function() {
JST.video({
model: this.model
});
return this;
}
});
App.Views.PlayListView = Backbone.View.extend({
el: $("#playlistWrapper"),
initialize: function(videos) {
_.bindAll(this, 'render');
this.modelViews = $.map(videos.models, function(model, i) {
return new App.Views.VideoView({
model: model
});
})
this.render();
},
render: function() {
var that = this;
$(this.el).clear();
$.each(this.modelViews, function(i, modelView) {
$(that).el.append(modelView.render().el);
});
return this;
}
});
i am always getting below error
$(this.el).clear is not a function
[Break On This Error] $(this.el).clear();
it seems my el of PlayerListView is empty.
i have div with id playlistWrapper.
if i use jquery selector for playlistWrapper it gives proper element.
what am i doing wrong.
I'm a little late to the party on this, but the problem is that you're specifying a jquery selector before the DOM is loaded.
A backbone object is defined with an object literal passed in to the extend method. For example, the following are functionally the same:
MyView = Backbone.View.extend({
el: "#foo"
});
var viewObj = {el: "#foo"};
MyView2 = Backbone.View.extend(viewObj);
values on the right-hand side of a key/value pair in an object literal are parsed and executed immediately. this means that a jQuery selector used for el will be parsed as soon as you declare it, not when the view is instantiated. chances are, you have your javascript file included in your app and it's being downloaded before the DOM is loaded, so the jquery selector can't find the element you're referring to.
There are a number of things you can do to work around this.
you can call $(this.el) whenever you need to use the element
you can set this.el in the view initializer
you can set {el: $("#whatever")} as a parameter to the view constructor, assuming the view is constructed after the DOM has loaded
you can use the javascript module pattern to defer definition of the views and other backbone objects until after the DOM is loaded
and probably a handful of other options that i'm not thinking of at the moment
Well clear is not a jQuery function... You might be looking for empty?
Comments on your code:
In you video view:
no need to assign the model from the options, this is done for you
you might want to append the result of the templating (JST) to this.el otherwise nothing will show up...
In your playlist view:
in your render, in your each loop, change $(that).el to $(that.el)
since you define el as a jQuery, you do not need to use $(this.el) over and over
use this.$el.clear();
and update jquery.js file like
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"> </script>
and first Bind Initialize then el: bind.

Resources