Block helper renders directly to view?

Hey all!

I’ve been scratching my head on this one for a bit. I’m trying to write a block helper that wraps some content with some additional HTML, but when I call the block it seems to render directly to the view. Here’s a fiddle:

Edit fiddle - JSFiddle - Code Playground

  • Expected output: “before during after”
  • Actual output: “during before undefined after”

I’m following what Handlebars says to do for block helpers… anyone have an idea what’s going wrong?

Love, Erik

1 Like

The call to

options.fn(this)

will just render the contents of the helper to the buffer. To wrap the content you can push to the buffer before and after calling options.fn(). Not sure if this is the best solution, but it seems to work. Here’s a working fiddle:

http://jsfiddle.net/Qu84y/

Cheers, Ray

This topic highlights an important point with helpers that needs coverage in the docs:

The preferred way to register helpers in Ember is with Ember.Handlebars.helper as seen in the guides. This delegates down to Ember.Handlebars.registerBoundHelper under the hood, ensuring data bindings and observers are wired up correctly.

However, Ember.Handlebars.helper does not support blocks when used in this form:

Ember.Handlebars.helper('wrap', function() { ... });

Instead, the idiomatic way to achieve what you’re after is to create a specialised view class and register a helper for that class:

App.WrapView = Ember.View.extend({
  layout: Ember.Handlebars.compile('before {{yield}} after')
});

Ember.Handlebars.helper('wrap', App.WrapView);

Fiddle here: Edit fiddle - JSFiddle - Code Playground

This is not strictly equivalent to the previous example, as the additional view will have a tag of its own that can be determined with tagName and other Ember.View options, but it sounds like that might be what you’re after anyway.

1 Like

Additional:

Just checked, and both the options.data.buffer.push approach and custom view approach work fine with bindings. They’re both good to use, so I guess it depends on the desired HTML output as to which you go for. Although the former is not officially documented, but perhaps it should be.

@jgwhite wow, that’s very clean! Although ideally I could pull the template from HTML, like…

<script type="text/x-handlebars" data-template-name="wrap">
  before {{yield}} after
<script>

App.WrapView = Ember.View.extend({
  layout: Ember.TEMPLATES['wrap']
});

But that doesn’t work.

I also neglected to mention that my helper is a conditional helper, so I don’t see how I can just write out the template as a string like in your example. I would need to do something like this:

<script type="text/x-handlebars">
  {{#wrapIf bacon}}
    during
  {{/wrapIf}}
<script>

App.WrapIfView = Ember.View.extend({
  layout: function(context, options) {
    if (context == "bacon") {
      return 'before {{yield}} after';
    }
  }
});

Which also doesn’t work. @rmbrad your buffer flushing solution works, so thank you! I can definitely move forward. Feels wrong to use an undocumented call, and I would love to get the code into a template/view as @jgwhite was suggesting. But I can’t see how.

Will take another look in the future if I have time.

Thanks all! Erik

So the problem with doing:

App.BaconView = Ember.View.extend({
  layout: Ember.TEMPLATES['wrap']
});

…is that Ember won’t have added any templates to Ember.TEMPLATES yet.

Luckily, it seems like the Ember team already considered this use case:

App.IfBaconView = Ember.View.extend({
  layoutName: 'if-bacon-layout'
});

Ember.Handlebars.helper('ifBacon', App.IfBaconView);
<script type="text/x-handlebars" data-template-name="if-bacon-layout">
  {{#if view.bacon}}
    {{yield}}
  {{else}}
    No bacon, no yield.
  {{/if}}
</script>
<script type="text/x-handlebars">
  {{#ifBacon baconBinding="userHasBacon"}}
    Thanks for the bacon!
  {{/if}}
</script>

In this example you’re probably better off rendering a partial, but you get the gist. :smile:

In the future, these sorts of questions are best asked on Stack Overflow. Thanks!

This topic is now invisible. It will no longer be displayed in any topic lists. The only way to access this topic is via direct link.