How does Backbone View render work? - javascript

I have a model's view set up as follows:
var PlayerView = Backbone.View.extend({
el: "div.player",
template: _.template($("#playerTemplate").html()),
render: function() {
this.$el.append(this.template(this.model.toJSON()));
return this;
}
});
and the main view is set up as follows:
var AppView = Backbone.View.extend({
el: "#players",
render: function() {
var pView = new PlayerView({model: playerModel});
this.$el.append(pView.render().$el);
}
});
If you notice the render of AppView, I am rendering the PlayerView first, and then appending its $el to AppView's $el. I was expecting an error situation here, as it would display PlayerView 2 times to main View as follows:
First, in the pView.render() where I put content in pView and
Second, in the same line where I append pView's $el to main view.
But it just appends the pView only once. How does this work?
I am not sure I have explained my question clearly; I can add more context if required.

Assuming div.player exists in DOM as you mentioned in comments,
When you do pView.render(), it adds the template inside it.
Then when you append pView's element (div.player) to AppView's element (#players), entire div.player is moved into #players.
Your code is working the way it should work.
If you intent to create multiple players, You shouldn't use el option in player view, Instead you should decorate the element created by backbone and create multiple instances of player view.

Related

Why is .$el needed, and what does it point to?

This is probably more of a javascript question, but I'm going through this backbone tutorial and was wondering why is there a .$el at the end of this line, and which element is it point to?
self.$el.append((new BlogView({ model: blog})).render().$el);
Here is the full code below.....
var BlogsView = Backbone.View.extend({
model: blogs,
el: $('.blogs-list'),
initialize: function() {
this.model.on('add', this.render, this);
},
render: function() {
var self = this;
this.$el.html('');
_.each(this.model.toArray(), function(blog) {
self.$el.append((new BlogView({ model: blog})).render().$el);
});
return this;
}
});
$el is a reference for the DOM element for the view. Backbone views are not DOM elements themselves, they are generic javascript objects which have a property called $el which holds the DOM element which is what you actually see on the webpage. You can think of a backbone view as a controller of sorts for the DOM element, and when you add event listeners to your view, define render, etc, you are always acting on its DOM element stored in $el ($el is the same DOM element as el, the former just plays nicely with jQuery). In this case, your view is BlogView and if we break new BlogView({ model: blog})).render().$el up:
new BlogView - creating an instance of your view, backbone will automatically create a DOM element for your view and store it in yourView.$el
.render() - telling the view to render its HTML inside the $el element. In Backbone, our render function is where we generate HTML markup/format data and "draw" the view by shoving this markup into our view's $el.
render().$el - render() returns this which is just our view itself, so calling render().$el is like saying "render my view and return my DOM element.
self.$el.append(..) - this block of code is thus given our DOM element $el which then inserts it.
so putting it all together we get: new BlogView({ model: blog})).render().$el which first creates our view, renders our view and returns our view's DOM element which can be appended to the page, manipulated, etc.

Getting my backbone views to load test data?

This is a new question but runs on from the last one, Dynamically load my 'IDs' into my Backbone Collection?
I now have all my models getting the data from my database. Now I want to be able to load this data into a view. But for some reason I can not get any views to work at all, this is my current view code,
var MyView= Backbone.View.extend({
el: '.page',
render: function() {
this.$el.html('CONTENT HERE FROM BACKBONE');
return this;
}
});
var testView = new MyView({});
$(document).ready(function(){
$('.page').append(testView);
})
Now '.page' is a div tag with that class set up on my page. But will not output my test text above, so what am I doing wrong?
The main aim is to have this view load the data form the model, which is all working fine, but right now I can not even get this simple test to work?
Have I forgotten to do with starting up the view functions with backbone?
All help most welcome.
Glenn.
you can initialize View inside document ready, View automatically call his render function when in initialize state, but in your case since document dose not load completely in initialize state, it fail to render
var testView;
$(document).ready(function(){
testView = new MyView
});
testView is an instance of MyView. You should not be calling append with testView. You need to invoke render method on testView instance. Since MyView el element is .page, content will be append to .page.
Use this:
$(document).ready(function(){
testView.render();
})

backbone.js changing el value of views before render is called

I have 3 different views, each of which share this same el value:
el:"<div id='main-inner'>"
and in the render function:
this.$el.html(html);
I was asked to combine all of these onto one page, so I'm trying to make a master view that has a collection of these views and renders each of the views within it. The problem is they each write to the same element, #main-inner and clear it's html before appending it's own html value. What is the proper way to change the el value before rendering from the master/wrapper view? Is this the best way to go about it?
Thanks!
Hand over the el to the view when you initialize your views:
var view1 = new myView({el:'#div1'});
var view2 = new myView({el:'#div2'});
var view3 = new myView({el:'#div3'});
And in your view:
var myView = Backbone.View.extend({
initialize: function(options){
this.el = options.el;
}
});
Now you have Views, each rendering in it's own el.
Hope this can help.
You can define your el as global and use it.
example:
App.Common.el = "";
use :
App.Common.el

Backbone.js render() never called

Simple view (reduced code):
My.View = Backbone.View.extend({
className: 'my-view',
initialize: function() { },
render: function() {
console.log('render');
return this;
}
});
I use it in another view as subview like this:
var myView = new My.View();
this.$el.append(myView.render().$el);
First instead of a className the view had a template and the view was rendering fine.
However when I removed the template and added className instead it is not rendering correctly.
It just renders the div and the correct class name however no logging in the render method is performed. AND when I add some html inside the render method to this.$el it never appears. Any ideas why?
UPDATE:
When I put my custom rendering code inside the afterRender method it works. Why is it not possible to overwrite the render method in my case?
this.$el.append(myView.render().el);
I would render the subview the following way.
var myView = new My.View();
this.$el.append(myView.el);
myView.render();

Backbone.js EL and Template in the View

So i'm very new to backbone.js and not so good at JavaScript in general, so I was wondering if someone could explain to me why
I cannot define my EL property, and Template property in my view, and then use this.template in my render. Instead I have to define the template and el in my render function.
var ProductView = Backbone.View.extend({
el: $('#product-list'),
initialize: function() {
this.el.html('<span style="color:white">loading...</span>');
}, // end initialize
render: function(collection) {
// // assign the template
this.template = $('#product_template');
// Where the template will be placed
this.el = $('#product-list');
// Add the collection to the main object
this.collection = collection;
// add tthe data to the html variable
var html = this.template.tmpl(this.collection.toJSON());
// place the html in the element.
this.el.html(html);
// not even sure what the hell this is.
return this;
} // end render
});
The problem isn't in the way you're defining el or template, it's in how you're setting the call back. In Workspace, your router, you're setting the callback for your collection refresh event like this:
// Bind the view and collection
// So when the collection is reset, the view executes the render method
Products.bind("reset", this.view.render);
The problem is, you're setting a method as a callback, but you're not providing a context object as the third argument to bind - so the method is called, but this in the method refers to the global object, not the view. So this.el is undefined, because it's not looking at the view instance at all. Try:
// Bind the view and collection
// So when the collection is reset, the view executes the render method
Products.bind("reset", this.view.render, this.view);
and see how that goes.
(I made a jsFiddle to demonstrate that the el and template were set properly under normal circumstances, though it doesn't actually include the fix above, which is hard to mock up without the server-side data: http://jsfiddle.net/nrabinowitz/QjgS9/)
You can't do this:
var ProductView = Backbone.View.extend({
el: $('#product-list'),
// ...
and get anything useful in el as #product-list probably isn't even present in the DOM when your ProductView is built; so trying to use $('#product-list') for el is simply the classic "I forgot to use $(document).ready()" problem dressed up in Backbone. Using $('#product-list') for el should work if #product-list is around when you define your ProductView though.
You can do this though:
var ProductView = Backbone.View.extend({
el: '#product-list',
// ...
and then say $(this.el) when you need to do things inside your view methods. Not only is $(this.el) the usual way of using el but it also works and that's sort of important.
The same issues apply to #product_template.
Looking at your code I see this:
// INstantiate the view
this.view = new ProductView();
// Bind the view and collection
// So when the collection is reset, the view executes the render method
Products.bind("reset", this.view.render);
Presumably the render is being triggered by the reset event. But, and this is a big but, the render method isn't bound to the right this anywhere so this won't be the ProductView when render is called and this won't have anything that you expected it to; hence your bizarre "undefined" error.
You could use _.bindAll in your initialize:
initialize: function() {
_.bindAll(this, 'render');
// ...
but usually you'd want to give the view a collection when you create it and the view would bind itself to the events so your structure will still be a bit odd.
You can also supply a context (AKA this) when you call bind:
collection.bind('reset', this.render, this);

Categories

Resources