Login/Register
How can I yield multiple pieces of content into an ember.js component template?
Asked Answered
A

2

17

The goal is to define a structure of HTML that has more than one block of content that is declared by the caller. For example, a header, body, and content. The resulting markup should be:

<header>My header</header>
<div class="body">My body</div>
<footer>My footer</footer>

The template instantiating the component would define each of the three sections, My header, My body, and My footer.

With Ruby on Rails, you would use content_for :header to capture the header content from the caller, and yield :header to interpolate it.

Is this possible in ember.js?

Alleviator answered 17/5, 2015 at 1:20 Comment(0)
A
19

As of ember v1.10, yield accepts parameters. However handlebars doesn't yet allow for inline comparisons of variable values. By defining some properties on the component, we can get pretty close to what rails does.

Per the above example, the component's template would look like:

<header>{{yield header}}</header>
<div class="body">{{yield body}}</div>
<footer>{{yield footer}}</footer>

And the component definition would resolve the variable arguments to the yield statements:

export default Ember.Component.extend({
  header: {isHeader: true},
  footer: {isFooter: true},
  body:   {isBody: true}
});

This means that {{yield header}} is actually yielding an object {isHeader: true} to the consuming template. So we can use a nested if/else structure to declare the three sections like this:

{{#my-comp as |section|}}
  {{#if section.isHeader}}
    My header
  {{else if section.isBody}}
    My body
  {{else if section.isFooter}}
    My footer
  {{/if}}
{{/my-comp}}
Alleviator answered 17/5, 2015 at 1:20 Comment(1)
This can be further simplified to remove the need for the object definitions in the component. By using the ember-truth-helpers addon, you can use {{yield 'header'}} in the component template and then {{#if (eq section 'header')}} in the consumer template.Alleviator
B
6

The previous reply may be out-to-date.

There is an accepted RFC for this problem; the Named Templates Block API will support passing multiple blocks to a component.

As of Ember 2.3, Contextual components allows another approach to this case:

Split your component into multiple subcomponents and pass the subcomponents back to the component as block parameters; this setup allows to set the content of each subcomponent block.

Check out the Twiddle - full example.

// my-component.js

{{yield (hash
   header = (component 'my-header')
   content = (component 'my-content')
   footer = (component 'my-footer')
)}}
{{#unless hasBlock}}
   {{my-header}}
   {{my-content}}
   {{my-footer}}    
{{/unless}}

// my-{header/content/footer}.js

{{#if hasBlock}}
   {{yield}}
{{else}}
   Default xxxxx
{{/if}}

In this case, you could use the default component content or pass the specific content to any subcomponent like:

{{my-component}}



{{#my-component as |f|}}
  {{f.header}}
  {{#f.content}}
    Custom content
  {{/f.content}}
  {{f.footer}}
{{/my-component}}


{{#my-component as |f|}}
  {{#f.header}}
     Custom header
  {{/f.header}}
  {{#f.content}}
     Custom content
  {{/f.content}}
  {{#f.footer}}
     Custom footer
  {{/f.footer}}
{{/my-component}}

This solution does not force the component API/structure, then the component could be wrongly used if a subcomponent is omitted, added multiple times, or have the wrong order, in these cases the component will generate an undesired content.

Check out the Twiddle - full example.

Bronchiole answered 20/9, 2018 at 12:22 Comment(2)
This should be the accepted answer for Ember 2.x and 3.xPendant
Additionally, instead of hardcoding the yielded subcomponents, the parent component my-component can accept them as parameters.Pendant

© 2022 - 2024 — McMap. All rights reserved.