Handlebars Block helper (based on templating language Twig)


#1

Hi,

I wanted to share a handlebars helper I wrote for solving a common problem when using templates. Let’s say you have an application template (application.hbs) which has a navigation bar. Now, you want to change some elements inside this navigation bar based on the current route you are in. You can do this very nicely with this block helper.

GitHub page with more info: https://github.com/steffenbrem/ember-helpers

JSBin example: http://jsbin.com/yepube/3/edit

If anyone got a suggestion, I am glad to hear it.


#2

Is this basically like a multiple #with ? Couldn’t you just pass in an object to with? or am I missing something?


#3

As far as I can see, what this is doing is allowing to ‘rewrite’ template contents from other templates. Only first mention of block is actually rendered, but all subsequent calls are just replacing previous block contents.

Is this right?


#4

Yes, you are correct.


#5

Basically, this is used when you want to render a template outside it’s current context.

When you have the following template (application.hbs):

<h1>My application</h1>
<nav>
    <!-- This will be the placeholder for the block "actions" and is rendered -->
    {{#block "actions"}}{{/block}}
</nav>
<div id="content">
    {{outlet}}
</div>

And if you then want to replace the template that is rendered inside the block “actions”. For instance, inside another template (users/index.hbs):

{{#block "actions"}}
    <button {{action "delete"}}>Delete all</button>
{{/block}}

<h3>Users overview</h3>

When you enter the users/index route, the block “actions” will automatically be replaced with the block template inside users/index.hbs. When you leave the users/index route, the block “actions” will go back to it’s original state.


#6

So that’s basically named outlet/yield…, but with a default. So you can have multiple outlets, essentially… is that right? LoL. Seems very complicated.

It’s probably worth your while making your explanation a lot simpler and maybe creating a jsbin for explanation. It’s probably a really common use case, I reckon! :slight_smile: I know of at least one stack overflow question asking how to do this. :smile:

Nice! :slight_smile:


#7

Yes, you could see it as multiple outlets (for the same outlet/route though). I am also planning on adding something like: {{parent}} which would render the parent block in place. So you get something like this:

{{#block "navigation_actions"}}
    <!-- Render the parent "navigation_actions" block in place -->
    <div>{{parent}}</div>

    <button>Tan</button>
{{/block}}

#8

I’ve tried to use your code as a point of reference for something that I might be overcomplicating. I was wondering if I could get some feedback. The code is currently published at kennethkalmer/ratchet-ember.

I’ve been running around in circles, and just cannot get my block behavior to work. I keep ending up with the following exception: You can't use appendChild outside of the rendering process. Not a lot of results on Google either, which is why I’m bringing this up here to help future devs too…

My idea is to have different types of block helpers that map to the different types of bars that Ratchet provides, the first example looks something like this:

{{#title-bar}}
  <a href="#/" class="btn btn-link btn-nav pull-left">
    <span class="icon icon-left-nav"></span>
    Back
  </a>
  <h1 class="title">Custom title bar</h1>
{{/title-bar}}

The helper stashes the view in a header controller, which feeds it back to another view.

I’m using an implicit controller at the moment, but it would probably be converted to an explicit controller once I get more behaviors tied in…

Any help would be greatly appreciated.


#9

@steffenbrem you need a very clear example.

You also might want to talk to Aaron Sikes on this issue: https://github.com/emberjs/ember.js/issues/3334#issuecomment-41506958

Seems a little cross-purposes / similar? 'course I could be conflating because I don’t properly understand either of these?


#10

Here is a small JSBin example: http://jsbin.com/yepube

Editable (the edit button of jsbin is behind the navbar): http://jsbin.com/yepube/3/edit

Hope this makes things more clear :). The only thing to check out is the html part (templates) of the JSBin, the javascript part is only for making ember and the helper work.


#11

@steffenbrem ah okay I finally understand what it does. Thanks for the example. Quite clear. :slight_smile:

So this is, effectively, a named yield/outlet into a layout from within a template… right?

Your implementation makes it quite tricky to know what’s going on, IMHO. (Which is possibly because of the naming). However, that could simply be beacuse I haven’t seen a great use case where you couldn’t and wouldn’t use something else. (Your use case didn’t really sell it to me, unfortunately).

As far as I can see, a conditional in the template based on a property could fulfil most requirements that this does, yet be much simpler and easier to understand.

I’d love to hear a great use-case for this that you just couldn’t use something else simpler for, but so far I can’t see one. :frowning:


#12

Yeah, I guess you are right that the use cases are not well spread. But this can be very useful for those tiny problems, then you DO need something like this. For instance, see this screenshot: http://puu.sh/8oaLk.png. I had a drop down menu where I could select the current application that I’m managing. I could not put it inside the left sidebar (when on a small device this left sidemenu is 40px wide or something, not much). So the best place would be the navigation bar, you can see this in the screenshot.

Now, because of the block helper I can use the context of the current route and display it outside. I guess that is the only use case I can think of too XD.


#13

Just out of curiosity, why can’t you just use a named outlet?


#14

How would you implement this behaviour with named outlets? You have to implement/override renderTemplate and use something like this?:

this.render("users.header_actions", {
    outlet: "header_actions",
    into: "application"
});

So if i have the named outlet inside application.hbs it would render users/header_actions inside it with any context you like of course. Is this what you mean?

It looks that my helper is more like a shortcut then :stuck_out_tongue: But I think the names outlets way is not a very nice way to implement it. It’s not very clear what belongs to what. You have to go inside the routing logic in order to know what is happening. But maybe thats a personal preference.


#15

Yeah that’s my understanding of it.

The current state of the API feels very irregular and messy, though. The addition your talking about seems to muddy the waters more rather than clear them… I wonder if there’s a way we can get your use-case requirements fulfilled but also clarifying and reducing the things that a new user has to learn…?