Multiple yield blocks with contextual components in Ember.js

A component in Ember.js normally takes only one block. However, with contextual components (available since Ember.js 2.3), you can achieve a similar functionality as if you had multiple block arguments to a component. The use case where I needed this is for a popover. In this blog post I will show how you can make a component that accepts multiple yield blocks by means of the popover example.

Popover?

I feel the terminology used by people for tooltips, popups, popovers and so on is not always so clear. What I mean with a popover is what Bootstrap calls a popover: when a particular button is clicked (the “trigger”), it opens a “popover” dialog next to it. I think the following Ember-Twiddle makes it clear what I mean. The twiddle also contains the working implementation for the example described here.

Why do we need multiple yield blocks?

In this popover example, you could get away without any block components at all. Let’s assume you have a component pop-over that accepts two properties as follows:

This can definitely work! However, it is quickly limiting if you want to add markup to your button label (e.g. highlight a specific word) or dialog content (e.g. split the text into multiple paragraphs). If you follow this path, you’d have to define a property on the containing controller that would contain the HTML markup as strings and then process them in the popover component with Ember.String.htmlSafe. We are not using React.js here so we want to keep our templates separate from the Javascript code :-).

The solution of course, is having multiple yield blocks on the pop-over component. This way, we can add as much markup as we want to both the button label and the dialog content.

How does this work?

A popover has both a trigger and a dialog with the content. Therefore, we need to define three components:

  • pop-over: represents the entire popover “widget”
  • pop-over-trigger: represents the button. It has a click handler and passes that action up to the pop-over component.
  • pop-over-dialog: represents the dialog. Its visibility is controlled by the pop-over component.

First, let’s see how we use the component.

One first thing that you can notice is that the implementation is well encapsulated, meaning that the user of the component does not know about click handlers or visibility controls. He can safely put the content of the trigger and the dialog without having to implement any pop-over logic himself. We can improve the internal implementation of the component (e.g. by extending with a “close on click outside” functionality, or a fade-in animation for the dialog) without changing the interface to the outside.

A second important advantage is that it’s very declarative: the meaning of the different blocks is very clear for someone who sees this for the first time.

Now how do we implement this in the component?

The contextual components syntax allows us to choose which components will be rendered and with what input and action handlers. For instance, specifying the onClick handler directly here encapsulates the logic so that the users of the component don’t have to worry about it.

This template is the hardest part and is the crux of how it works. But once you understand its mechanics, you will be able to write much more powerful components in your own app.

If you want to see how the other components work, I refer you to the working Ember-Twiddle.

Want to know more about contextual components?

I learn a lot by reading the source code of Ember addons and figure out how they do certain things. Regarding contextual components, the ember-form-for addon uses them extensively to render the various form elements so it’s interesting to see how they are being used there.

As always, feel free to leave comments below if you have any questions and I will try to answer them!

Write us your thoughts about this post. Be kind & Play nice.