Contextual Components in Ember

Ember 2.3 has just been released and with it we're receiving an exciting enhancement to components - contextual components. The feature will be very useful when creating complex nested component structures. It's especially handy for addon developers.

Motivation

Before this enhancement developers had to break component encapsulation in situations where several components were to be connected into a complex UI. For instance, we'd like to share data between nested components:

{{#some-form data=model}}
  {{some-field data=model.name}}
{{/some-form}}

As you can see, we have to pass the model into the two components separately - even though our plan was for these components to be linked. The new contextual components allow us to simplify the above into something like this:

{{#some-form data=model.name as |form|}}
  {{form.field}}
{{/some-form}}

This is achieved by defining the some-form component's template like this:

// app/templates/components/some-form.hbs
{{yield (hash
  field=(component 'some-field' data=data)
)}}

We can now simply yield components with the use of the hash helper.

A working example

My first thought upon reading the new contextual components API was to create a tooltip/popover component. This component would yield two child components:

  • One to define the element, which - upon clicking - should reveal the popover
  • Another one to hold the content of the popover

We'll create a popover-container component with the following template and logic:

// app/templates/components/popover-container.hbs
{{yield (hash
    handle=(component 'popover-handle' click=(action 'toggle'))
    content=(component 'popover-content' isShowing=isShowing)
)}}
// app/components/popover-container.js
import Ember from 'ember';

export default Ember.Component.extend({  
  isShowing: false,

   actions: {
    toggle() {
      this.toggleProperty('isShowing');
    }
  }
});

The component has an isShowing property which can be toggled between the true and false states with the toggle() action. Its template yields a hash containing two child components:

  • popover-handle will trigger the action when clicked
  • popover-content is passed the isShowing property, so that it knows when to be displayed

The popover-handle does not need to contain any logic for this example and its template is simply {{yield}}. popover-content similarly does not have any logic, but the template is slightly modified:

// app/templates/components/popover-content.hbs
{{#if isShowing}}
  {{yield}}
{{/if}}

Finally we can combine the above:

// app/templates/application.hbs
{{#popover-container as |popover|}}
    {{#popover.handle tagName="button"}}
      Toggle
    {{/popover.handle}}
    {{#popover.content}}
      Content
    {{/popover.content}}
{{/popover-container}}

We make the popover.handle component into a button, which - when clicked - will reveal or hide what's defined in popover.content. The popover-container component can share actions and data with its child components quite easily!

Here's an Ember Twiddle if you want to see this example in action: https://ember-twiddle.com/4ae6c7817d92e2cb001d

Summary

This post was not meant to be a full-blown tutorial. I just wanted to introduce a great new feature, which should make our web applications better and easier to maintain and develop.

Ember just keeps getting better and better!

Kevin P. Kucharczyk

Read more posts by this author.

Kraków, Poland