So far I have been using EJS templates in my Rails/Backbone.js application.
I really want to start using backbone.marionette. What can I do to make it work with EJS?
MyView = Backbone.Marionette.ItemView.extend({
template: "#some-template"
});
It says in the docs that I need to provide a jQuery selector to the template attribute but I don't think that my EJS templates have one.
Update:
This is how I would use templates to render my views normally:
TasksTree.Views.TaskItem = Backbone.View.extend({
...
render: function() {
...
this.$el.html(JST['tasks_tree/item'](options));
return this;
}
})
And I have templates folder with item.jst.ejs file that looks like that:
<li>
<label><%= taskTitle %></label>
</li>
My templates folder is included in application.js
There's a section in the docs that shows several examples of replacing the rendering mechanism, as well:
http://derickbailey.github.com/backbone.marionette/#backbone-marionette-renderer/custom-template-selection-and-rendering
Seeing that JST provides the templates for you, and you don't need to cache them in any other way, though, you could skip past most of the functionality built in to Marionette's Renderer object and replace the render function entirely.
Backbone.Marionette.Renderer.render = function(template, data){
return JST[template](data);
}
You would also replace the use of teh template attribute on views with the template path instead of a jquery selector:
Backbone.Marionette.ItemView.extend({
template: "tasks_tree/item"
});
Hope that helps. If not, let me know.
I found this reference to be quite helpful: https://github.com/marionettejs/backbone.marionette/wiki/Using-jst-templates-with-marionette
You could replace the render function with something along these lines, which provides better error handling and flexibility.
Marionette.Renderer.render = function(template, data) {
if(typeof template === 'function') {
return template(data);
}
else {
if(!JST[template]) throw "Template '" + template + "' not found!";
return JST[template](data);
}
};
You can then specify the template path (as previously mentioned):
Backbone.Marionette.ItemView.extend({
template: "tasks_tree/item"
});
Or if your template is very simple, you can use a function to return just a string:
Backbone.Marionette.ItemView.extend({
template: function(data) {
return "<div>" + data.attribute + "</div>";
}
});
Related
I want to be able to set/get data of a template object when using template event handler functions. I have tried to set a variable at the point when the template is rendered and hoped it would be accessible later on, in this case when an element in the template is clicked by the users, but it isn't working:
<template name="fooBar">
<div class="some_element">CLICK ME</div>
</template>
Template.fooBar.rendered = function(){
this.templateVar = "Hello";
}
Template.fooBar.events({
'click .some_element': function(e,t){
alert(this.templateVar); // Should say 'Hello', but is 'undefined'.
}
});
Using reactive-dict package, you can do like this.
First add it.
meteor add reactive-dict
Second create the templates. (note im using meteor 1.1 version)
if (Meteor.isClient) {
Template.hello.onRendered(function(){
this.templateVar = new ReactiveDict(); //create the var templateVar
})
Template.hello.onRendered(function(){
this.templateVar.set("hi", "hello"); //give them a value
})
Template.hello.events({
'click .some_element': function(e,t){
console.log(t.templateVar.get('hi')) //and print the value using the template instance.
}
});
}
As per the suggestion given by Sindis, this is the quickest way to solve the issue:
Instead of...
alert(this.templateVar);
... just do...
alert(this.templateVar); or alert(Template.instance().templateVar);
I am trying to implement Typeahead with an elasticsearch backend. The search itself seems to be working, now I am trying to tweak the optic. I would like to use a Ember.Handlebars helper that I wrote. My first attempt was using handelbars as template engine:
App.SearchComponent = Ember.TextField.extend({
didInsertElement: function() {
App.UserSearchEngine.initialize();
this.initializeTypeahead();
},
initializeTypeahead: function(){
var _this = this;
this.typeahead = this.$().typeahead({
hint: true,
highlight: true,
minLength: 3
},
{
name: _this.$().attr('id') || "typeahead",
// template: 'My: {{firstName}}',
limit: this.get("limit") || 5,
source: App.UserSearchEngine.ttAdapter(),
templates:{
suggestion: Ember.Handlebars.compile('{{firstName}} {{lastName}}')
}
}
);
}
});
This is giving me an error:
Uncaught TypeError: Cannot read property 'container' of undefined
caused by Ember in the "_triageMustache" helper in the line
var helper = Ember.Handlebars.resolveHelper(options.data.view.container, property);
This is probably due to the fact that I try to compile a template directly.
If I would use Handlebars.compile() instead of Ember.Handlebars.compile() it would work. It seems that the context is not correct.
You actually should use regular Handlebars.compile here. The Typeahead library doesn't know anything about Ember bound templates, so it doesn't know how to invoke them.
Alternatively, your template is so simple it could just be a literal function, to save yourself the hassle of shipping a Handlebars compiler in production or precompiling this one specially:
templates: {
suggestion: function(context){ return context.firstName + ' ' + context.lastName; }
}
I'm trying to display a simple template, but it seems like the template doesn't get added, as there is nothing added to the DOM. The code is called for sure and a container has the method setTpl(tpl). What am I doing wrong? The sample above is the most simple example I could imagine, but it doesn't work!
Ext.define('MyApp.view.sample', {
extend: 'Ext.Container'
config: {},
initialize: function() {
this.callParent();
var sampleText = '<div> why?? </div>';
var t = new Ext.Template(
sampleText,
{
compiled: true
}
);
t.compile();
this.setTpl(t);
},
});
HTML = template + data. So your next step is to call setData. Check the docs for tpl. If what you want is to plug in some raw HTML that doesn't depend on data, you've got the html config (and the corollary method setHTML). Last advice, if that's just for rendering some HTML, you don't need to use a container, a Component would be enough.
You have created a class, but you also need to instantiate it. Try something like this:
Ext.create('MyApp.view.sample', {
renderTo: 'some-div-id',
// any other necessary config options
// (see http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.Container)
});
I'm using Handlebars templating with a Marionette.CompositeView. The template is defined as:
template : function (serializedData) {
var templFn = Handlebars.compile(myTemplateDef);
return this.templFn(serializedData);
}
In traditional Backbone with Handlebars it is suggested to not compile the template each time we render the view, but instead to store the compiled template as a View property so it only gets compiled once thus saving on resources:
templFn : Handlebars.compile(myTemplateDef),
render : function () {
var serializedData = this.model.toJSON();
...
this.$el.append(this.templFn(serializedData);
}
But in the Marionette case the context of template() is window and I don't control how/when template() is called.
So the question is: given that we don't want to create a global var window.templFn is there a way to separate the template compilation from its usage in the case of Marionette?
There's a handlebars plugin for marionette: https://github.com/asciidisco/Backbone.Marionette.Handlebars
It may not be entirely up to date, but you should at least be able to see how they are handling the compilation of templates.
In general, Marionette provides a Marionette.Renderer and Marionette.TemplateCache object to allow a template to be compiled once, and only once, and then re-rendered from that cached template compilation.
I just started working with Marionette just 2 days ago, and I use Handlebars in my code before marionette ( pure backbone ), then I used this way in marionette :
template : function(data) {
if (typeof this.tplFun === 'undefined') {
this.tplFun = Handlebars.compile($('#angry_cat-handlebars').html());
}
return this.tplFun(data);
};
or you can just add tplFun into your initialize function
I am trying to inject another component into an element that is rendered by the template of another Coomponent..but in the afterrender event, the template is yet to be rendered so the call to Ext.get(el-id) returns null: TypeError el is null.
tpl:
new Ext.XTemplate(
'<tpl for=".">',
'<ul>',
'<li class="lang" id="cultureSelector-li"></li>',
'</ul>',
'</tpl>'
),
listeners: {
afterrender: {
fn: function (cmp) {
console.log(Ext.get('cultureSelector-li')); // < null :[
Ext.create('CultureSelector', {
renderTo: 'cultureSelector-li'
});
}
}
},
So when can I add this component so that the element is targeting has been created in the DOM?
I think it depends on the component that you are working with. For example, the Data Grid View has a "viewready" event that would suite your needs, and depending what you are attempting, the "boxready" function could work for combo box (only the first render though). Other than that, you can either go up through the element's parent classes searching for the XTemplate render function being called (might be in the layout manager) and extend it to fire an event there, or risk a race condition and just do it in a setTimeout() call with a reasonable delay.
I ended up having to do the work myself. So, I now have the template as a property called theTpl, and then rendered it in beforerender, and then i was able to get a handle on the element in afterrender. This seems wholly counter-intuitive, does anyone have any insight?
beforeRender: {
fn: function (me) {
me.update(me.theTpl.apply({}));
}
},
edit in fact I just extended Component thus:
Ext.define('Ext.ux.TemplatedComponent', {
extend: 'Ext.Component',
alias: 'widget.templatedComponent',
template: undefined,
beforeRender: function () {
var me = this;
var template = new Ext.XTemplate(me.template || '');
me.update(template.apply(me.data || {}));
me.callParent();
}
})
...template accepts an array of html fragments
Turns out I was using the wrong things - apparently we should be using the render* configs for this type of thing (so what are thetpl & data configs for?)
Here's a working fiddle provided for me from the sencha forums:
http://jsfiddle.net/qUudA/10/