Series: EmberSchool Samples: Intermediate Components

Yielding Data into Block Components

Published on Jul 29, 2016

Code

//templates/about.hbs
{{#email-status pageName="'Bout" as |word word2|}}
  <p>{{word}}. This is only on the About page.</p>
  {{word2}}
{{/email-status}}
//templates/components/email-status.hbs
{{yield 'hello' 'world'}}
//components/collapsible-component.js
import Ember from 'ember';

export default Ember.Component.extend({
    isOpen: false,
    actions: {
      toggleOpen(){
        this.toggleProperty('isOpen');
      }
    }
});
//templates/components/collapsible-component.hbs
{{yield isOpen this}}
//templates/food.hbs
{{#each model as |food|}}
  {{#collapsible-component as |isOpen component|}}
    <div class="food-item {{if food.isAvailable '' 'not-available'}}">
      <div {{action 'toggleOpen' target=component}} class="title-bar">{{food.name}} <span class="pull-right">{{if isOpen 'V' '>'}}</span></div>
      {{#if isOpen}}
        {{icon-image food.imageUrl left=true}}
        <div class="description">{{food.description}}</div>
        {{#link-to 'foods.food' food.id}}
          <button>Show</button>
        {{/link-to}}
        {{#link-to 'foods.food.edit' food.id}}<button>Edit</button>{{/link-to}}
        <button {{action 'toggleAvailability' food}}>
          {{if food.isAvailable 'Make Unavailable' 'Make Available'}}
        </button>
      {{/if}}
    </div>
  {{/collapsible-component}}
{{/each}}
//controllers/foods.js
//...
actions: {
  //...
  toggleAvailability(food){
    food.toggleProperty('isAvailable');
    food.save();
  }
}

Transcript

In block components not only can you give it some HTML in handlebars to use within the component, you can feed it variables that come from the component. In this video, we’re going to learn how to do that.

We’ll start off with the simple example in the email-status. So we’re going to yield the word hello. And then on the about page, we’ll grab the word. The way we do it is we say as and then we have these pipes, and between these pipes are where we put the variables, the data that we’re using. And now we can put our word here. We can see that it’s yielded our word, hello. Now let’s yield a second word, and we’ll go ahead and call that word2, and we’ll see that also yields.

So you can put in as many things as you want into the yield block and you grab them as arguments here after as. And of course if you feed in anything to the component, it goes before the as.

Now let’s tackle something a little bit more ambitious. So in the food-item component, we have the isOpen property and the toggleOpen action. And those aren’t specific to it being food. Let’s go ahead and abstract those away from the food-item component into a collapsible-component component. Let’s create this step by step.

First we’ll create the component, collapsible-component in the command line. Then we’ll take the isOpen property and add it to the component, and we’ll put in the toggleOpen action. Then in the collapsible-component template, we’re going to yield isOpen. In the foods template, we’re going to put our collapsible-component component, and then we’re going to get isOpen from the component. And what we’re going to be putting within the block of collapsible-component is what’s currently in the template of food-item. So we’ll just grab all of this and paste it in there. And as you can see, it’s already using the isOpen boolean, and it was previously getting it directly from the food-item component, now it’s being passed through to the block.

Then we’ll see that everything is showing up like it was before. And to show that we’re getting this from the component, we’re going to set isOpen to true by default. And there we go, they’re all open at the start.

Now we’ll want to pass in the action toggleOpen so that we can use that in here. But that’s not quite as easy as just passing in a boolean in order to get at that action. In the collapsible-component we’re going to have to pass in the entire component, so we do that using this. So we’re going to pass in the entire component and we’re going to name that component. And then the action will be still toggleOpen because that’s what it’s called, but we’ll put the target as the component. So we’re not targeting foods controller like we would otherwise. We’re targeting the component that’s being passed out here which is the collapsible-component. Now we can click these and they are collapsible components. Awesome.

But there is one more problem to fix. If we hit 'Make Unavailable', we’ll get an error saying that nothing handled the action toggleAvailability. This is because we have the action toggleAvailability on the food-item component, but toggleAvailability when it’s called here is in the foods controller, so its target will be not the food-item component, not even the collapsible-component component, but the foods controller. So let’s go ahead and add that. And now it’s all working.

If you’re ever wondering which JavaScript file to put an action in, know that it will target the JavaScript file that matches up with the handlebars file that you’re in. So if we’re in the foods template for the controller, it will go into the foods controller. If we were in a component like we were with food-item, then it will go into that component. If we’re within a block component, then in order to get it to target that component, we have to specify that, since by default it will target whatever handlebars template it’s in. So that’s how we make a generic collapsible component, and of course don’t forget what we originally came here to learn, how to yield variables to a block component.

EmberSchool Samples: Intermediate Components

Subscribe to our mailing list