Reputation: 3374
I am having a template.html file like this:
<script type="text/template" id="form-template">
<input type="text" class="form-control input-sm" style="" id="company"
value="<% if(typeof company!=='undefined') {%><%=company%><% } %>" placeholder="Company name">
</script>
<script type="text/template" id="person-template">
<input type="text" class="form-control input-sm" style="" id="company"
value="<% if(typeof company!=='undefined') {%><%=company%><% } %>" placeholder="Company name">
</script>
Now in the backbone view.js I have two views like this
var FormView = Backbone.View.extend({
template: _.template($(formTemplate).filter("#form-template").html()),
render:
function(){this.$el.html(this.template(this.model.toJSON())}
});
var personView = Backbone.View.extend({
template: _.template($(formTemplate).filter("#person-template").html()),
render:
function(){this.$el.html(this.template()}
});
When i render the form view, it works fine but when I render the person view, i get the fields pre-populated with value as "[object HTMLInputElement]" even if i pass nothing as the data.
The obvious guess is that both the templates are using <%=company%> but why that should be a problem if the templates are completely separate from each other?
Upvotes: 1
Views: 1903
Reputation: 434665
You have two problems:
id="company"
so you should expect odd things to happen when you put both templates on the same page. id
attributes must be unique or you don't really have HTML anymore, you have something that sort of looks like HTML.<input id="company">
in the page, you have a window.company
property whose value is the DOM node for <input id="company">
.The second problem is what you're seeing but you should fix 1 anyway.
Underscore templates use JavaScript's with
to allow an object to act like a scope. If with
doesn't find the name (company
in this case) in the supplied object (which will be {}
in the second template), then it goes up the scope chain trying to find a variable called company
. Since you have <input id="company">
already in the DOM, with
will find window.company
as company
. That leaves you with a DOM node in company
and typeof company
won't be 'undefined'
so your template throws a stringified DOM node on the page.
Consider this simplified example:
<script type="text/template" id="form-template">
<input type="text" id="company"><br>
</script>
<script type="text/template" id="person-template">
<%= typeof company %><br>
<%= typeof company !== 'undefined' ? company : '' %>
</script>
and then a little JavaScript to mirror what your Backbone code is doing:
var f = _.template($('#form-template').html());
var p = _.template($('#person-template').html());
$('body').append(f());
$('body').append(p());
console.log(window.company);
You should see something like this on the page:
<input ...>
object
[object HTMLInputElement]
and a DOM node in the console.
Demo: http://jsfiddle.net/ambiguous/sRnuw/
This is a rather insidious problem with no clean solutions that I can think of. Possible options:
Make sure the id
s on your form elements don't match any properties in your templates.
Make sure you supply all the properties to your templates, even empty ones. This will let you short-circuit the with
before it goes looking for things in window
.
Use the variable
option to _.template
:
By default, template places the values from your data in the local scope via the
with
statement. However, you can specify a single variable name with the variable setting.
See the _.template
docs for examples that use the variable
option.
with
trickery. I tend to use Handlebars.Upvotes: 4