When I create view backbone creates empty div-container if el is not set. Template (this.$el.html(this.template(this.model.toJSON())))
inserted in that div. How to avoid this wrapper? I need clean template without any wrappers so I can insert it anywhere I want? It's not reasonable to call jobView.$e.children()
with many elements.
<script id="contactTemplate" type="text/html">
<div class="job">
<h1><%= title %>/<%= type %></h1>
<div><%= description %></div>
</div>
</script>
var JobView = Backbone.View.extend({
template:_.template($("#contactTemplate").html()),
initialize:function () {
this.render();
},
render:function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var jobView = new JobView({
model:jobModel
});
console.log(jobView.el);
I think the real answer to this question has not been provided yet, simply remove the div
from the template and add the className
property to JobView
! This will result in the markup you require:
The template:
<script id="contactTemplate" type="text/html">
<h1><%= title %>/<%= type %></h1>
<div><%= description %></div>
</script>
The view:
var JobView = Backbone.View.extend({
className: 'job', // this class will be added to the wrapping div when you render the view
template:_.template($("#contactTemplate").html()),
initialize:function () {
this.render();
},
render:function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
When you call render
you will end up with the desired markup:
<div class="job">
<h1><%= title %>/<%= type %></h1>
<div><%= description %></div>
</div>
I think the best solution to this is:
render: function(){
var html = this.template(this.model.toJSON()));
var newElement = $(html)
this.$el.replaceWith(newElement);
this.setElement(newElement);
return this;
}
Sorry For a late response and this may have already been solved. I find its best to try and make templates and views as easily as possible. I use Handlebars for my templates.
Each of your templates regardless of use will need to have an HTML element associated with them, so in your view pick whatever elements you want and remove that element from your template, instead of trying to go against the grain. You can then set the attributes in your view to mirror those of your removed template element.
var yourview = Backbone.View.extend({
{
tagName : *whatever element ( i.e. section, div etc ),
attributes : {
id : 'your element id'
class : 'your element class'
etc
},
})
Then in your template remove that element this this element will be created nicely without wrapping your template, rather than having to change your render method.
You can render the view into any container you like either specifying the $el property yourself or using the setElement()
method before calling render:
var jobView = new JobView({
model:jobModel
});
jobView.setElement($('#your_selector_here'));
console.log(jobView.el);
If you look at the docs for View.el
you'll see that you can also specify the el
property either when writing your view, or by passing a parameter in the constructor.
I have had this same problem with Backbone. In my opinion it is a design flaw. Here is a Reddit post describing some of the solutions possible: http://www.reddit.com/r/javascript/comments/11pkth/how_do_you_render_your_backbonejs_views/
Here is Jeremy Ashkenas' take on the issue:
| If I want to completely encapsulate the HTML inside of my template, without creating any extra divs, | I must replace this.el. At least as far as I know. Is there any better way to do this?
Give up your desire to do that, and you'll have a much easier time ;)
A big part of the point of Backbone always providing a view's element ("el") for you, is that your events are valid at all times -- regardless of whether the view is in the DOM, if the data is ready yet, or if the template is available. It's a more stateless way to declare your mouse and keyboard events, relying less on the required ordering of your rendering.
If you really want to replace a view's element, use setElement. But it's not recommended, or necessary.
For better or worse, in Backbone you are expected to use the el
provided by the view, and tweak it to your liking with the tagName
, className
, id
and attributes
properties of that view.
The obvious problem with this approach is that you mix and match JS and HTML like there is no tomorrow. In my opinion, it is better to keep things nicely separated; the characteristics of the el
should be specified along with the template.
In 2014, I have written a drop-in component which aims to solve the issue: Backbone.Declarative.Views. It's probably the most underrated of all my open-source stuff, but it does the job just fine.
How?
Include Backbone.Declarative.Views into your project. Then, put those el
-defining properties into data attributes of your template.
<script id="my-template"
type="text/x-template"
data-tag-name="p"
data-id="myContainer"
data-class-name="someClass orOther">
<!-- template content here -->
</script>
Now, if your view has a template: "#my-template"
property, its el
is set up as
<p id="myContainer" class="someClass orOther"></p>
And that's it, really. For the full details, check out the docs.
Why data attributes?
Using data attributes solves the basic problem of keeping the HTML-related stuff in your HTML files and templates, and out of your Javascript. At the same time, this approach is fully compatible with the way things are usually done in Backbone.
Ie, all existing Backbone templates continue to work as they should, just as all those tagName
definitions scattered throughout Javascript still get applied as usual. Conflicts with other Backbone extensions and plugins are avoided, and there is no need to change legacy code. That makes Backbone.Declarative.Views safe to include into virtually any project.