Backbone underscorejs add value to template - templates

I have a simple add event in Backbonejs, I also have template:
initialize: function()
{
this.template = _.template($("#person-template").html());
}
renderperson: function(model)
{
//model.name is "Jack" for example
//Now I want to render this template with Name replaced with "Jack"?
$("#somelement").append( ); //What to do here???
}
template is simple:
<script type="text/template" id="person-template">
<div class="person"><%= Name %></div>
</script>

$("#somelement").append( this.template(this.model.toJSON()) );
you may also need to add
_.bindAll(this)
to your initialize method

may be this can help you:
http://backbonejs.org/#View-render
var Bookmark = Backbone.View.extend({
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});

Related

BackboneJs unable to render template in rendered template

I have two views and I want to render a second one inside the first one.
I am doing this:
First view:
var EventContainer = Backbone.View.extend({
el: '#sectionContainer',
initialize: function() {
this.render();
},
render: function() {
var that = this;
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {}); //Option to pass any dynamic values to template
$(that.el).append(that.template); //adding the template content to the main template.
}, 'html');
new EventList();
return this;
}
});
Template for the first view:
<div class="main-content pull-left">
<ul id="eventList" class="event-list no-list-style no-padding"> </ul>
</div>
Second view:
var EventList = Backbone.View.extend({
el: '#eventList',
initialize: function() {
var that = this;
this.EventsCollection = new EventsCollection();
this.EventsCollection.fetch({
success: function(data) {
that.collection = data;
that.setTemplate();
},
error: function() {}
});
},
setTemplate: function() {
var that = this;
$.get('templates/home/eventList.html', function(data) {
that.template = _.template(data);
that.render();
}, 'html');
},
render: function() {
var that = this;
this.collection.each(function(eventData) {
var eventTemplate = that.template(eventData.toJSON());
console.log('---> ' + $(that.el));
$(that.el).append(eventTemplate);
});
return this;
}
});
Template for the second View:
<li class="event-item">
<small><%= like_count%> </small>
</li>
When I try to render the second view, el is undefined.
What m I doing wrong?
Thanks.
If you have a look at this.el inside EventList#initialize, I think you'll find that it is undefined rather than the <ul> that you're expecting it to be. Why would that be? Where does #eventList come from? #eventList comes from EventContainer's template and that won't be in the DOM until this AJAX call:
$.get('templates/home/eventContainer.html', ...)
completes. That $.get won't complete until well after your new EventList();. When you instantiate your EventList, it will go looking for #eventList but won't find anything, that will leave you with this.el being undefined. Then later on, that $.get will return and #eventList will show up in the DOM; this is why $('#eventList') works.
The easy solution is to defer instantiating your EventList until after you've added the necessary things to the DOM:
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {});
$(that.el).append(that.template);
new EventList(); // <-----------------------
}, 'html');
If you're going to do this, then you could make the dependency explicit by dropping the el from the EventList definition and specifying it when you create the view instead:
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {});
$(that.el).append(that.template);
new EventList({ el: that.$('#eventList') });
}, 'html');

How to pass arguments to a controller init method?

How can I pass external values into a controller. In the below code I would like to pass in values filtertype and filterterm from PostsController into PostsDynamicController. What is a way do achieve this?
I have a template like this
<script type="text/x-handlebars" id="posts">
{{view Ember.Select
contentBinding="App.names.content"
valueBinding="App.names.selected"
}}
{{view Ember.TextField valueBinding="filterterm" }}
<button {{action "submit"}} > Submit</button>
{{outlet}}
</script>
Part of my App.js is this:
App.PostsController = Ember.ObjectController.extend({
content: [],
filterterm: "",
submit: function () {
var filtertype = App.names.selected;
var filterterm = this.get('filterterm');
this.transitionToRoute("posts.dynamicfinder");
}
});
App.PostsDynamicController = Ember.ObjectController.extend({
init: function () {
//want access to filtertype and filterterm here so that I can pass them in find. i.e.
//App.Request.find(filtertype: filterterm);
this.set('model', App.Request.find(..);
}
});
You cannot pass args to the controller's init() function.
To pass external values into a controller you should use bindings. Specifically the controller's needs property. See the ember guide dependencies-between-controllers
So for example:
// Change handlebars template to valueBinding="filtertype" instead of valueBinding="App.names.selected"
// Also these should be ArrayControllers not ObjectControllers
App.PostsController = Ember.ArrayController.extend({
filterterm: null,
filtertype: null,
submit: function () {
this.transitionToRoute("posts.dynamicfinder");
}
});
App.PostsDynamicController = Ember.ArrayController.extend({
needs: ['posts'],
termBinding: 'controllers.posts.filterterm',
typeBinding: 'controllers.posts.filtertype',
filteredPosts: function() {
var filtertype = this.get('type');
var filterterm = this.get('term');
// ...
}.property('term', 'type')
}
});

Define Model for Ember Component used in a bunch of different Routes?

I would like to be able to define the model for a component template inside the Ember.Component js instead of inside the route where the component is sitting. I have not seen any examples which are doing this...
Here I have my component template:
<script type="text/x-handlebars" id="components/info-box">
<div class="infoBox box">
<p>
<label>
{{preUnits}}
</label>
<span>
{{value}}
</span>
</p>
</div>
</script>
And here is how I am placing it inside one route template:
{{info-box title='Total Area' dataDef='buddhaData:DataGet/site/areaNum'}}
What I would like to do is use my relevant Ember.Component to do some stuff with the parameters of the info-box and then return a model for it.
App.InfoBoxComponent = Ember.Component.extend({
buildIt: function(){
var container = $('#' + this.get('elementId') );
var title = this.get('title');
var preUnits = this.get('preUnits') || '';
var dataDef = this.get('dataDef');
// Do stuff with dataDef.
var model = {
preUnits: '$',
value: 5000
}
// Hopefully return model somehow.
},
didInsertElement: function(){
this.buildIt();
}
});
I want to be able to use this component inside a bunch of different routes, and I do not want to have to refer to the route that a particular info-box is inside of in order to give the info-box its model, is this possible, or should I use some other feature, like a regular template and the render helper?
Once you have the model object, just set properties on the component itself:
App.InfoBoxComponent = Ember.Component.extend({
buildIt: function(){
var container = $('#' + this.get('elementId') );
var title = this.get('title');
var preUnits = this.get('preUnits') || '';
var dataDef = this.get('dataDef');
// Do stuff with dataDef.
var model = {
preUnits: '$',
value: 5000
}
// Set component's preUnits and value properties directly
this.setProperty('preUnits', model.preUnits);
this.setProperty('value', model.value);
// or
this.setProperties(model);
// Hopefully return model somehow.
},
didInsertElement: function(){
this.buildIt();
}
});
You should use render if you'd like to define which model you want to use (if the model is different than the current context). If it's the same context, you should just use partials. You could also generate helper and pass in the model to that.
Ember.Handlebars.helper('autocomplete', Ember.View.extend({
templateName: 'controls/autocomplete',
filteredList: function() {
var list = this.get('list'),
filter = this.get('filter');
if (!filter) { return list; }
return list.filter(function(item) {
return item.name.indexOf(filter) !== -1;
});
}.property('list.[]', 'filter')
}));
Usage:
<script type="text/x-handlebars" data-template-name="application">
{{autocomplete list=list1}}
{{autocomplete list=list2}}
</script>
<script type="text/x-handlebars" data-template-name="controls/autocomplete">
<p>{{input type="text" value=view.filter}}</p>
<ul>
{{#each view.filteredList}}
<li >{{name}}</li>
{{/each}}
</ul>
</script>
Full example

How to override a property so it returns a different value if the property is empty?

I am probably going to use an ArrayController + itemController setup to solve this, but maybe this is better off inside the model layer.
I want to override the property of an object to return another value if the property is empty. I think this i best described in code (jsfiddle: http://jsfiddle.net/ahx_/Tqw4C/2/).
App = Ember.Application.create()
App.Teacher = Ember.Object.extend()
App.Pupil = Ember.Object.extend({
// TODO Add a property ('answer') that returns teacher.answer unless this.answer is defined
// Pseudo-code:
// answer: function(key, value) {
// if(Ember.isEmpty(this.answer)) {
// return this.get('teacher.answer')
// } else {
// return this.answer
// }
// }.property('answer')
})
App.IndexController = Ember.Controller.extend({
init: function() {
this._super()
teacher = App.Teacher.create({answer: 'correct'})
this.set('pupil1', App.Pupil.create({ teacher: teacher, answer: 'incorrect' }))
this.set('pupil2', App.Pupil.create({ teacher: teacher }))
}
})
You need to add another property as .property() cannot refer to itself.
Object:
App.Pupil = Ember.Object.extend({
answerToShow: function(){
return this.get('answer') ? this.get('answer') : this.get('teacher.answer')
}.property('answer')
})
Template:
<script type="text/x-handlebars" data-template-name="index">
Pupil1 ('incorrect'): {{pupil1.answerToShow}}
<br>
Pupil2 ('correct'): {{pupil2.answerToShow}}
</script>
Demo: http://jsfiddle.net/Tqw4C/5/

How to use this.template if app.js is not in document ready?

I tried to follow http://ricostacruz.com/backbone-patterns/#inline_templates to avoid http://ricostacruz.com/backbone-patterns/#abuse however I have a typical view like this:
// in app.js
App.MyView = Backbone.View.extend({
className: "ui-widget-content",
template: _.template($("#myTemplate").html()),
render: function()
{
this.$el.html(this.template(this.model.toJSON()));
}
Then I just include app.js like this
<script src="./js/jquery-1.7.2.min.js"></script>
<script src="./js/jquery-ui-1.8.20.custom.min.js"></script>
<script src="./js/underscore.js"></script>
<script src="./js/backbone.js"></script>
<script src="./js/app.js"></script>
Browser complains that the $("#myTemplate") line in App.MyView.template is null (because document is not ready?). What shall I do?
Why not lazy-load your templates? Compile the template on first use and cache the compiled template in the view's "class". You could even add your base view to handle this caching with something like this:
var BV = Backbone.View.extend({
template: function(data) {
if(!this.constructor.prototype._template)
this.constructor.prototype._template = _.template($('#' + this.tmpl_id).html());
return this._template(data);
}
});
Then you could have things like this:
var V = BV.extend({
tmpl_id: 'tmpl',
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
and the #tmpl template would be compiled the first time it was used and it would be compiled at most once.
Demo: http://jsfiddle.net/ambiguous/hrnqC/
Notice the no wrap (head) in the demo and have a look at the console to see what is being compiled and how often.
my quick fix for this was to compile the template on view initialisation..
App.MyView = Backbone.View.extend({
className: "ui-widget-content",
template: '#myTemplate',
initialize: function(){
this.template = _.template($(this.template).html());
},
render: function(){
this.$el.html(this.template(this.model.toJSON()));
}
then everything else still works the same and you can put your render method in a base class..
The easiest thing to do is simply not cache the template:
App.MyView = Backbone.View.extend({
className: "ui-widget-content",
render: function()
{
var template = _.template($("#myTemplate").html())
this.$el.html(template(this.model.toJSON()));
}