Embed partial templates with Backbone / Marionette / Underscore templates - templates

In Marionette with Underscore templates, is it possible to 'include' one template file into another template file - so that common markup can be shared across multiple templates?
We use requireJS to load the .tpl files as and when - and then assign a tpl file to a Marionette view;
View.MyView = Marionette.ItemView.extend({
template: 'parent_template.tpl'
});
Where 'parent_template.tpl' contains something like.
<h1>A Parent View</h1>
<button class='btn-one'>One</button>
<button class='btn-two'>Two</button>
<button class='btn-three'>three</button>
What I would like to do is extract say the buttons to a common tpl, that other tpl's could use, something like...
Where 'parent_template.tpl' contains something like.
<h1>A Parent View</h1>
{{child_template.tpl}}
And 'child_template.tpl' contains something like.
<button class='btn-one'>One</button>
<button class='btn-two'>Two</button>
<button class='btn-three'>three</button>
And then lots of other templates could pull in the shared template.
Any ideas?

The way that I solved this was with templateHelpers.
Load the partial templates as per usual with require JS.
Then pass a reference to the child template into the parent template via the templateHelper;
define(['tpl!parent.tpl', 'tpl!child.tpl'], function (ParentTemplate, ChildTemplate) {
View.MyView = Marionette.View.extend({
template: ParentTemplate,
templateHelpers: function() {
return {
childTpl: ChildTemplate
}
}
});
}
The parent template includes the child via the helper
<h1>parent template</h1>
<%= childTpl(obj) %>
The child can access the data if you pass 'obj' - the template model
<h2>this is the child, that can access the parent data <%= title %></h2>

For me it smells odd including template in template.
You can instead use Mariontte.LayoutView with subview:
buttons subview :
Buttons = Marionette.ItemView.extend({
template: 'child_template.tpl'
});
layout :
MyView = Marionette.LayoutView.extend({
template: 'parent_template.tpl',
region : {
buttonRegion : '.myButtonsRegion'
},
onShow : function(){
var buttons = new Buttons();
this.buttonRegion.show(buttons);
}
});
and then parent_template.tpl :
<h1>A Parent View</h1>
<div class="myButtonsRegion"></div>

Related

Different layouts depending on sub resources

Sorry if this is a really obvious questions but I have the following routes:
Web.Router.map(function () {
this.resource('orders', function(){
this.resource("order", {path:":order_id"});
});
});
And for my orders template I have something like:
<div class="someclass">
{{outlet}}
</div>
And what I want todo is:
{{#if onOrderRoute}}
<div class="someclass">
{{outlet}}
{{else}}
<div class="someotherclass">
{{/if}}
</div>
I was wondering what the best way of doing this is, or am I mising something?
There are multiple ways to accomplish this. The view has a layoutName property you can use to specify your layout. Another option is to specify a property on your child view, and then your template can bind to that by using the view property.
For example:
Web.OrderView = Ember.View.extend({
childView: true
);
Then, in your template you bind to view.childView
{{#if view.childView}}
<!-- code goes here -->
{{/if}}
Further, you can even create a mixin and then just inject that mixin into every view.
Web.ChildViewMixin = Ember.Mixin.create({
childView: true
});
Web.ChildView = Ember.View.extend(ChildViewMixin, {
});

Populating nested underscore templates in Backbone.js

I am attempting to create an html page from a complex JSON object. I have already successfully parsed the JSON object into a Collection of Models, Where each Model has a collection of another Model etc..
I therefore have nested views to cater for this.
To create my html page, I have two templates like the following:
<script type="text/template" id="template1">
<h1><%=heading1%></h1>
<h2><%=heading2%></h2>
<ul id="template2-list"></ul>
</script>
<script type="text/template" id='template2'>
<p class = "heading"><%-otherheading%></p>
<div class="content" id="tab">
.....
</div>
</script>
As you can see, I have a template (template1) that contains a list of template2. How would I go about populating these templates from my Backbone nested views?
This is what I have tried:
var CollectionView = Backbone.View.extend({
type: "CollectionView", //For debugging purposes
el: "#container",
initialize: function () {
},
render: function () {
_.each(this.model.models, this.process, this);
return this;
},
process: function(obj)
{
var childItemView = new View1({model: obj});
childItemView.render();
this.$el.append(childItemView.el); //This works fine since I only have one Model in the highest level collection
}
})
var View1 = Backbone.View.extend({
type: "View1",
template: _.template($("#template1").html()),
tagName: "div",
className: "tableRow",
initialize:function () {
this.model.on("change", this.modelChanged, this);
},
render: function () {
var outputHtml = this.template(this.model.toJSON());
this.$el.html(outputHtml);
this.model.get('nestedModel').each(this.process, this);
return this;
},
process: function(obj) {
var childItemView2 = new View2({model: obj});
childItemView2.render();
childItemView2.el = '#template2-list';
$(this.el).append(childItemView2.el); //This still results in template2 being placed after the entire template 1
},
modelChanged: function(model, changes) {
console.log("modelChanged: " + this.model.get('title'));
}
});
If it's just populating underscore, then you should convert the collection to json(including the submodels collections), and you can add a for loop inside of the template. <% for(var x... %>.
The other option is, to use a library like marionette which has a composite view which can hold collection views, you can see an example for a treeView here: http://lostechies.com/derickbailey/2012/04/05/composite-views-tree-structures-tables-and-more/
it basically shows how to render collections inside collections.
There are lots of ways to do this.
Template inside template
Pass the entire collection and do all recursive iteration logic in the template itself by calling the child template inside parent template itself. Only one view is involved.
<script type="text/template" id="template1">
<h1><%=heading1%></h1>
<h2><%=heading2%></h2>
<ul id="template2-list">
<!-- Your iteration logic goes here -->
<%= _.template($("#template2").html())({model: model}) %>
</ul>
</script>
<script type="text/template" id='template2'>
<p class = "heading"><%-otherheading%></p>
<div class="content" id="tab"></div>
</script>
Better way is:
In the collection view, create a child view instance(you have done that)
Do the recursive iteration logic to read the collection models in the collection view(parentview) and call the child view to render the child collections.
If you want a complete solution, create a fiddle with json and html. Will help you to make it work.
I realised my mistake. Not sure if this is entirely correct, but I rendered the parent view first, then found the new list element (template2-list) and appended the rendered child view to that.
i.e.
render: function () {
var outputHtml = ...
this.$el.html(outputHtml); //render parent view
this.model.get('nestedModel').each(this.process, this);
...
},
process: function(obj) {
var childItemView2 = new View2({model: obj});
childItemView2.render();
this.$('#template2-list').append(childItemView2.el);
},
Thanks for all the help!

Emberjs Rendering other Templates within another template

I would like to render the template 'location' within a sidebar in the template 'permit'.
My code is a replica of the code shown on emberjs.com's rendering templates example but the 'location' template is either not loading at all, or loading just the 'locations' template in repeat but not rendering the original html in the 'permit' template. So the location.hbs is displayed but not inside the sidebar it's been assigned to.
Here's some code :D
<!-- Right Sidebar (a small piece of the 'permit' template) START -->
<div id="sidebar-wrapper" class="super-super-float-right-col">
<div id="sidebar-wrapper" class="super-float-right-col">
<div id="sidebar-wrapper" class="float-right-col">
{{outlet location}}
</div>
</div>
</div>
<!-- Right Sidebar END -->
VpcYeoman.PermitRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('location');
}
});
router.js
VpcYeoman.Router.map(function () {
...
this.resource('permit', { path: '/permit/:permit_id' });
...
});
http://emberjs.com/guides/routing/rendering-a-template/ is once again being vague and of little help.
emberjs community assemble!
You've created a named outlet, but aren't telling it to render into that named outlet. The code you are using is saying render the location template, instead of the default template (permit). You would do something along the lines below
this.render('location', { // the template to render
into: 'permit', // the route to render into
outlet: 'location', // the name of the outlet in the route's template
//controller: 'blogPost' // the controller to use for the template
});
Thanks for the help, kingpin!
http://emberjs.com/guides/routing/rendering-a-template/ is quite misleading when compared to the solution.
What is written in the PermitRoute must be the same as what is written in the .hbs file, with the addition of a colon.
renderTemplate: function() {
this.render({ render:'location'});
}
so what would be put into the HTML would not be {{outlet location}}, as their website claims, which will render only 'location' and not 'permit' but {{render 'location'}} will render a template within a template.

Base class attribute names for templates with no explicit ember view

So in Ember it's awesome that you don't necessarily need to extend a view for a template and instead one can automagically be created for your template.
My question is, for the base HTML element of a templates automagically created view how can I specify a class name so I can namespace the CSS?
// template /////////////////
<script type="text/x-handlebars" data-template-name="contacts">
...
</script>
// html /////////////////
<div id="ember337" class="ember-view MyCoolClassName"> <---- custom class name in here
...
</div>
I know I can do it by extending a view for this template but to simply add an additional class name that seems overkill. Thoughts?
I think that isn't possible, because the root tag of a view, like this:
<div id="ember337" class="ember-view MyCoolClassName">
is generated from, your properties:
SomeView = Ember.View.create({
elementId: 'ember337',
classNames: ['ember-view', 'MyCoolClassName'],
tagName: 'div'
});
If you don't want to create a view to add a simple class, you can do:
<script type="text/x-handlebars" data-template-name="contacts">
<div class="MyCoolClassName">
Hello world
</div>
</script>
And in your css:
.ember-view .MyCoolClassName {
/*some style */
}

Is it possible to pass variables to a mustache partial

Lets say I have a mustache button partial like this
<button name="{{name}}" class="btn">{{title}}</button>
Is it possible somehow to set the title when calling the partial directly like this
<form>
....
{{> button|name=foo,title=Cancel}} {{> button|name=bar,title=Submit}}
</form>
This would make it much easier to create views instead of something like this and creating a hash for each button.
<form>
....
{{#buttons}}
{{> button}}
{{/buttons}}
</form>
I am not sure you can do that but I worked around that using mustache functions
javascript
var partial = "<div>{{value}}</div>";
var data = {some_data:[1,2,3]};
var items = {func:function (){
return function (index){
return mustache.render(partial,{value : data.some_data[index]});
}
});
mustache.render(main,items);
mustache
{{#func}}1{{/func}}
{{#func}}2{{/func}}
It's not possible to do it in plain Mustache, but it's doable in Handlebars with helpers. Handlebars is an extension of Mustache, so you can switch to it just by replacing parser library. Here's how it might look in Handlebars:
JS Helper (there are also implementations for many other languages):
Handlebars.registerHelper('button', function(title, options) {
var attrs = Em.keys(options.hash).map(function(key) {
key + '="' + options.hash[key] + '"';
}).join(" ");
return new Handlebars.SafeString(
"<button " + attrs + ">" + title + "</button>");
});
Usage in template:
{{buttton title name="name" class="class"}}
More info is available here: http://handlebarsjs.com/
No, you cannot pass variable view data directly from the template using Mustache.
However, rendering your buttons as a section using a list is a suitable alternative as of current when using Mustache:
Partial: (buttons.mst)
{{#buttons}}
<button name="{{name}}" class="btn">{{title}}</button>
{{/buttons}}
Template:
<form>
...
{{> buttons}}
</form>
View:
buttons: [
{ name: 'foo', title: 'Cancel' },
{ name: 'bar', title: 'Submit' },
]
Output:
<form>
...
<button name="foo" class="btn">Cancel</button>
<button name="bar" class="btn">Submit</button>
</form>
Which actually works quite well since if no buttons were passed, the partial won't be rendered.
Hope it helps.