4

I'm trying to make a simple block helper but can't find any documentation for ember-cli on the subject

UPDATED

Here's the helper:

import Ember from 'ember';

export default function uiInput(options) {
  return new Handlebars.SafeString(
    '<div class="ui input">'
    + options.fn(this)
    + '</div>');
}

And in the template:

{{#ui-input}}
  TEST
{{/ui-input}}

And the output should be:

<div class="ui input">
  TEST
</div>

But the output I'm getting is:

TEST
<div class="ui input">
  undefined
</div>

What am I doing wrong?

slightlytyler
  • 370
  • 3
  • 11
  • Any reason it has to be a helper? It seems like what you're doing would be a better fit for a component. – Peter Brown Dec 21 '14 at 12:49
  • 1
    Maybe but this is a simplified version of my real problem. Regardless, I think there are situations where a block helper is the answer and I'd like to know how to make one. Just updated the question with new details – slightlytyler Dec 21 '14 at 21:31

2 Answers2

2

Original answer (see below for the ember way)

I first want to try to explain what is happening here: Ember (currently 1.9.1) does not return rendered templates as strings any more for quite some time now when calling options.fn directly (despite all of the code samples out there that rely on this behavior). Instead, your content is rendering 'itself', meaning that it uses options.data.view's appendChild function to put things where they belong. You can see this in action e.g. by following the with helper's code down to line 81 in ember-handlebars/lib/helpers/binding.js. You can also find a previous discussion on this in this thread.

So, what can you do about it? We need to create a custom view to take care of what we need. The following code works for me using ember-cli. In app/helpers/ui-input:

import Ember from 'ember';

var UiInputView = Ember.View.extend({
    classNames: ['ui', 'input']
});

export default function(options) {
    var view = options.data.view;
    var viewOptions = {
        shouldDisplayFunc: function() {return true;},
        template: options.fn,
        inverseTemplate: options.inverse,
        isEscaped: !options.hash.unescaped,
        templateData: options.data,
        templateHash: options.hash,
        helperName: 'block-helper'
    };
    var childView = view.createChildView(UiInputView, viewOptions);
    view.appendChild(childView);
}

Maybe not all the viewOptions are necessary...

I hope this helps.

Update: the ember way

When I came here to answer this question, I was stubbornly determined that I should be able to write my own helpers. Little did I know about the {{yield}} helper. The correct code would thus look like this:

Component:

// app/components/ui-input.js
import Ember from 'ember';
export default Ember.Component.extend({
    classNames: ['ui', 'input']
});

Template:

{{!-- app/templates/components/ui-input.hbs --}}
{{yield}}

Usage:

{{#ui-input}}
    Test
{{/ui-input}}
suluke
  • 683
  • 5
  • 14
  • Marking you as the correct answer for the detailed explanation. The real take away is that block helpers are no longer supported and a component should be used instead. – slightlytyler Jan 19 '15 at 17:13
  • Thanks for marking my answer as correct. I think you are also right by saying it is not really supported. They would have docs for this otherwise. I still find it frustrating, though, since I work with block helpers all the time on the server side and they exist in htmlbars, so why not making them extendable? https://github.com/emberjs/rfcs/blob/master/active/0002-block-params.md also suggests, that they should be in the future. – suluke Jan 20 '15 at 18:05
0

I'm on ember-cli 0.1.4. When i try your ui-style helper in block notation:

{{#ui-input}}
  TEST
{{/ui-input}}

i get a javascript error in the console saying this:

Assertion failed: registerBoundHelper-generated helpers do not support use with Handlebars blocks.    

however, this works for me with a slight tweak to your helper:

helper:

import Ember from 'ember';

export default function uiInput(input) {
  return new Handlebars.SafeString(
    '<div class="ui input">'
    + input
    + '</div>');
}

template:

{{ui-input 'TEST'}}

getting this output:

<div class="ui input">
  TEST
</div>
benzo
  • 160
  • 1
  • 6