I am confused about why '''el''' is undefined here, and $el is defined.
The background is en experiment with CoffeeScript as follows:
class FastTodo.Views.AddTodoItem extends Backbone.View
template: JST['todo_items/add_item']
el: $('#main')
render: ->
console.log("render")
console.log($("#main"))
console.log(#el)
console.log(#)
$(#el).html #template
initialize: ->
#render()
How can I render the view in this case?
Try rewriting the element declaration to el: '#main'
I think that should work well for you.
By the way, according to your console log the jQuery element ($el) is empty as well. You must be declaring the view before the markup has been fully loaded. By giving el the selector for the elements you make sure it will be fetched only when the document is ready (loaded).
Related
I 'm trying to develop a simple RSS app using backbone.js. I 'm using this backbone.js tutorial. I 'm getting the following error, on line 2(template), when defining the template.
Can someone also tell me why is tagName: "li" defined in the tutorial?
uncaught TypeError: Cannot call method 'replace' of undefined
backbone.js
Javscript
window.SourceListView = Backbone.View.extend({
tagName:"li",
template: _.template($('#tmpl_sourcelist').html()),
initialize:function () {
this.model.bind("change", this.render, this);
this.model.bind("destroy", this.close, this);
},
render:function (eventName) {
$(this.$el).html(this.template(this.model.toJSON()));
return this;
},
close:function () {
$(this.el).unbind();
$(this.el).remove();
}
});
HTML
<script type="text/template" id="tmpl_sourcelist">
<div id="source">
<a href='#Source/<%=id%>'<%=name%></a>
</div>
</script>
thanks
You're getting your error right here:
template: _.template($('#tmpl_sourcelist').html()),
Part of _.template's internals involves calling String#replace on the uncompiled template text on the way to producing the compiled template function. That particular error usually means that you're effectively saying this:
_.template(undefined)
That can happen if there is no #tmpl_sourcelist in the DOM when you say $('#tmpl_sourcelist').html().
There are a few simple solutions:
Adjust your <script> order so that your #tmpl_sourcelist comes before you try to load your view.
Create the compiled template function in your view's initialize instead of in the view's "class" definition:
window.SourceListView = Backbone.View.extend({
tagName:"li",
initialize:function () {
this.template = _.template($('#tmpl_sourcelist').html());
//...
As far as tagName goes, the fine manual has this to say:
el view.el
[...] this.el is created from the view's tagName, className, id and attributes properties, if specified. If not, el is an empty div.
So having this in your view:
tagName: 'li'
means that Backbone will automatically create a new <li> element as your view's el.
This is the code:
NewEntry_CategoryView = Backbone.Marionette.ItemView.extend({
template: "#NewEntry_Category-template",
tagName: "p",
initialize: function () {
$("#sliderContainer").slider();
}
});
NewEntry_CategoriesView = Backbone.Marionette.CompositeView.extend({
template: "#NewEntry_Categories-template",
tagName: "div",
itemView: NewEntry_CategoryView,
itemViewContainer: '#categoryContainer',
appendHtml: function (collectionView, itemView) {
collectionView.$("#categoryContainer").append(itemView.el);
}
});
Why does the jquery ui slider not render when I show the NewEntry_CategoriesView ?
DOM events/manipulation like slide() won't have any effect on the view object's initialization because there is no such DOM element available yet.
Instead, you need to listen to dom:refresh of the view to manipulate its DOM element.
So, just put the code in onDomRefreshin your ItemView
onDomRefresh: function(){ $('#sliderContainer').slide() };
This above is a direct fix. But there are two more things to improve:
Don't call other div outside of this view when possible. In this case, if #sliderContainer belongs to another view, send an event to allow it slide itself. This is not the job of CategoryView. If it is inside current view, refer it with this.$el.find(".some-div") or better yet ui object.
Your collectionView's appendHtml is unnecessary. Marionette also takes of this common case.
I have some code which defines a backbone view being loaded as soon as a web page is loaded. The JavaScript is possibly executed before the DOM is fully loaded. If the dom element which becomes $el is not available when the code that defines the view is run is this a problem?
Programmatically I have something like this:
var view = Backbone.View.extend({
el: jQuery("#test")
events: {
}
render: function() {
this.$el.html();
}
//other view code
});
return view;
//some time passes
//with the view rendered above I now call :
view.render()
the problem is that when the render method is called this.$el is undefined. This is because when the first block of code was executed #test had not been loaded into the DOM. So; when the function Backbone.View.extend is called the 'el' element has to be loaded?
The context of this is a backbone application loaded via AMD. The first block of code is in a module. The module is 'required' before the DOM is loaded. Is this a common problem? How is it normally dealt with?
Thanks
el should not be a jQuery object but only a selector, $el will be the jQuery object for that selector. Also the id you specify as the el has to be found at initialization. So yes, the element has to be in the dom before you make it the el for the view.
If you're not creating / populating the view before making it a Backbone view you could do something like this to have backbone create the html tags etc. for you:
var view = Backbone.View.extend({
tagName: 'div',
id: 'test',
events: {
},
render: function() {
this.$el.html();
},
//other view code
});
You could then use templates to populate the view in your render function.
Short answer: Yes, the view element has to be loaded into the DOM when you initialize the view.
Why is the el undefined on a page refresh or page load? Is it because the el does not exict?
class ListView extends Backbone.View
className: 'channels-list'
el: '#main'
initialize: ->
console.log #el
# undefined
The element needs to be a tag name or element that already exists in the DOM. When a new view is created the _ensureElement function is called:
// Ensure that the View has a DOM element to render into.
// If `this.el` is a string, pass it through `$()`, take the first
// matching element, and re-assign it to `el`. Otherwise, create
// an element from the `id`, `className` and `tagName` properties.
_ensureElement: function() {
//...
}
To avoid this place the script tag below the element tag or wait for the document load event, for example using jQuery:
jQuery ->
class ListView extends Backbone.View
className: 'channels-list'
el: '#main'
initialize: ->
console.log #el
Or, as Vitaliy suggested, rest .el in the initialize function.
Exactly. If you define el it should be available in DOM structure
Inside initialize method you can set element again if it already exists in DOM:
#setElement $('#main')
I have model Post and collection Posts. And want to make form with list of all post in <select id="multi" multiple="multiple">. So i have to make a PostView render inside my #multi with just this template:
<option value=""><%= title %></option>
But finally I get it wrapped with div. Is there any solution for not wrapping this template with <div>?
If you don't define an el (or tagName) for the view (in the class or during instantiation) the view will be placed inside a div tag. http://documentcloud.github.com/backbone/#View-el
var PostView = Backbone.View.extend({
tagName: 'option'
});
UPDATE
Starting v0.9.0, Backbone has view.setElement(element) to get this done.
var PostView = Backbone.View.extend({
initialize: function() {
var template = _.template('<option value=""><%= title %></option>');
var html = template({title: 'post'});
this.setElement(html);
}
});
If you don't want to have the view wrap your HTML, you'll have to do a few things:
Replace this.el entirely
Call delegateEvents on the new el
render: function(){
var html = "some foo";
this.el = html;
this.delegateEvents(this.events);
}
Since Backbone generates a div or other tag (based on your tagName setting for the view), you have to replace it entirely. That's easy to do. When you do that, though, you lose your declared events because Backbone uses jQuery's delegate under the hood to wire them up. To re-enable your declared events, call delegateEvents and pass in your events declarations.
The result is that your view.el will be the <option> tag that you want, and nothing more.
In version 0.9.0, Backbone introduced view.setElement(element) to handle this operation.