Understanding Components in Ember 2

Share this article

This article was peer reviewed by Edwin Reynoso and Nilson Jacques. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Components are a vital part of an Ember application. They allow you to define your own, application-specific HTML tags and implement their behavior using JavaScript. As of Ember 2.x components will replace views and controllers (which have been deprecated) and are the recommended way to build an Ember application.

Ember’s implementation of components adheres as closely to the W3C’s Web Components specification as possible. Once Custom Elements become widely available in browsers, it should be easily to migrate Ember components to the W3C standard and have them usable by other frameworks.

If you’d like to find out more about why routable components are replacing controllers and views, then check out this short video by Ember core team members Yehuda Katz and Tom Dale.

The Tab Switcher Application

To get an in-depth understanding of Ember components, we will build a tab-switcher widget. This will comprise a set of tabs with associated content. Clicking on a tab will display that tab’s content and hide that of the other tabs. Simple enough? Lets begin.

As ever, you can find the code for this tutorial on our GitHub repo, or on this Ember Twiddle, if you’d like to experiment with the code in your browser.

The Anatomy of an Ember Component

An Ember component consists of a Handlebars template file and an accompanying Ember class. The implementation of this class is required only if we need extra interactivity with the component. A component is usable in a similar manner to an ordinary HTML tag. When we build our tab switcher component, we will be able to use it like so:

{{tab-switcher}}{{/tab-switcher}}

The template files for Ember components live in the directory app/templates/components. The class files live in app/components. We name Ember components using all lowercase letters with words separated by hyphens. This naming is by convention so we avoid name clashes with future HTML web components.

Our main Ember component will be tab-switcher. Notice I said main component because we will have several components. You can use components in conjunction with others. You can even have components nested inside another parent component. In the case of our tab-switcher, we will have one or more tab-item components like so:

{{#each tabItems as |tabItem| }}
  {{tab-item item=tabItem 
             setSelectedTabItemAction="setSelectedTabItem" }}
{{/each}}

As you can see, components can also have attributes just like native HTML elements.

Create an Ember 2.x Project

To follow along with this tutoial, you’ll need to create an EMber 2.x project. Here’s how:

Ember is installed using npm. For a tutorial on npm, you can see here.

npm install -g ember-cli

At the time of writing this will pull in version 1.13

ember -v
=> version: 1.13.8

Next, create a new Ember app:

ember new tabswitcher

Navigate to that directory and edit the bower.json file to include the latest version of the Ember, ember-data and ember-load-initializers:

{
  "name": "hello-world",
  "dependencies": {
    "ember": "^2.1.0",
    "ember-data": "^2.1.0",
    "ember-load-initializers": "^ember-cli/ember-load-initializers#0.1.7",
    ...
  }
}

Back in the terminal run:

bower install

Bower might prompt you for a version resolution for Ember. Select the 2.1 version from the list provided and prefix it with an exclamation mark to persist the resolution to bower.json.

Next start Ember CLI’s development server:

ember server

Finally navigate to http://localhost:4200/ and check the version your browser’s console.

Creating the Tab Switcher Component

Let’s create a tab switcher component using Ember’s built in generator:

ember generate component tab-switcher

This will create three new files. One is a Handlebars file for our HTML (app/templates/components/tab-switcher.hbs), the second is a JavaScript file for our component class (app/components/tab-switcher.js), the final one is a test file (tests/integration/components/tab-switcher-test.js). Testing the component is beyond the scope of this tutorial, but you can read more about that on the Ember site.

Now run ember server to load up the server and navigate to http://localhost:4200/. You should see a welcome message titled “Welcome to Ember”. So why isn’t our component showing up? Well, we haven’t used it yet, so lets do so now.

Using the Component

Open the application template app/templates/application.hbs. Add in the following after the h2 tag to use the component.

{{tab-switcher}}

In Ember, components are usable in two ways. The first way, called inline form, is to use them without any content inside. This is what we’ve done here. The second way is called block form and allows the component to be passed a Handlebars template that is rendered inside the component’s template wherever the {{yield}} expression appears. We will be sticking with the inline form throughout this tutorial.

This still isn’t displaying any content on the screen, though. This is because, the component itself doesn’t have any content to show. We can change this by adding the following line to the component’s template file (app/templates/components/tab-switcher.hbs):

<p>This is some content coming from our tab switcher component</p>

Now when the page reloads (which should happen automatically), you will see the above text displayed. Exciting times!

Create a Tab Item Component

Now that we have setup our main tab-switcher component, let’s create some tab-item components to nest inside it. We can create a new tab-item component like so:

ember generate component tab-item

Now change the handlebars file for the new component (app/templates/components/tab-item.hbs) to:

<span>Tab Item Title</span>
{{yield}}

Next, let’s nest three tab-items inside our main tab-switcher component. Change the tab-switcher template file (app/templates/components/tab-switcher.hbs) to:

<p>This is some content coming from our tab switcher component</p>

{{tab-item}}
{{tab-item}}
{{tab-item}}

{{yield}}

As mentioned above, the yield helper will render any Handlebars template that is passed in to our component. However, this is only useful if we use the tab-switcher in its block form. Since we are not, we can delete the yield helper altogether.

Now when we view the browser we will see three tab-item components, all saying “Tab Items Title”. Our component is rather static right now, so lets add in some dynamic data.

Adding Dynamic Data

When an Ember application starts, the router is responsible for displaying templates, loading data, and otherwise setting up application state. It does so by matching the current URL to the routes that you’ve defined. Let’s create a route for our application:

ember generate route application

Answer “no” to the command line question to avoid overwriting the existing application.hbs file. This will also generate a file app/routes/application.js. Open this up and add a model property:

export default Ember.Route.extend({
  model: function(){
  });
});

A model is an object that represents the underlying data that your application presents to the user. Anything that the user expects to see should be represented by a model. In this case we will add the contents of our tabs to our model. To do this alter the file like so:

import Ember from 'ember';

export default Ember.Route.extend({
  model: function(){
    var tabItems = [
      {
        title: 'Tab 1',
        content: 'Some exciting content for the tab 1'
      },
      {
        title: 'Tab 2',
        content: 'Some awesome content for the tab 2'
      },
      {
        title: 'Tab 3',
        content: 'Some stupendous content for the tab 3'
      }
    ];
    return tabItems;
  }
});

Then change the tab-switcher template file (app/templates/components/tab-switcher.hbs) to:

{{#each tabItems as |tabItem| }}
  {{tab-item item=tabItem }}
{{/each}}

Next, change the content of the tab-item template file (app/templates/components/tab-item.hbs) to:

<span>{{item.title}}</span>
{{yield}}

Finally change the tab-switcher usage in the application.hbs file to:

{{tab-switcher tabItems=model}}

This demonstrates how to pass properties to a component. We have made the item property accessible to the tab-item component template. After a page refresh, you should now see the tab item titles reflecting data from the models.

Adding Interactions Using Actions

Now let’s make sure that when a user clicks on a tab-item title, we display the content for that tab-item. Change the tab-switcher template file (app/templates/components/tab-switcher.hbs) to:

{{#each tabItems as |tabItem| }}
  {{tab-item item=tabItem setSelectedTabItemAction="setSelectedTabItem" }}
{{/each}}

<div class="item-content">
  {{selectedTabItem.content}}
</div>

This change assumes that we have a tabItem property on the tab-switcher component. This property represents the currently selected tab-item. We don’t currently have any such property so lets deal with that.

Inside a regular template, an action bubbles up to a controller. Inside a component template, the action bubbles up to the class of the component. It does not bubble any further up the hierarchy.

We need a way to send click actions to the tab-switcher component. This should happen after clicking any of its child tab-item components. Remember I said that actions get sent to the class of the component and not further up the hierarchy.

So it seems impossible that any actions coming from child components will reach the parent. Do not worry because this is just the default behavior of components and there is a workaround to circumvent it.

The simple workaround is to add an action to the tab-switcher template (app/templates/components/tab-switcher.hbs) like so:

{{#each tabItems as |tabItem| }}
  <div {{action "setSelectedTabItem" tabItem}} >
    {{tab-item item=tabItem setSelectedTabItemAction="setSelectedTabItem" }}
  </div>
{{/each}}

<div class="item-content">
  {{selectedTabItem.content}}
</div>

And to change the tab-switcher class file (app/components/tab-switcher.js) to look like

export default Ember.Component.extend({
  actions: {
    setSelectedTabItem: function(tabItem){
      this.set('selectedTabItem', tabItem);
    }
  }
});

At this point if you view our app in the browser, it will work as expected.

However, this workaround does not address the fact an action only bubbles up to the class of the component, so let’s do it in a way that does. Keep the changes in app/components/tab-switcher.js, but revert app/templates/components/tab-switcher.hbs back to its previous state:

<div class="item-content">
  {{selectedTabItem.content}}
</div>

{{#each tabItems as |tabItem| }}
  {{tab-item item=tabItem setSelectedTabItemAction="setSelectedTabItem" }}
{{/each}}

Now let’s change the tab-item template to:

<span {{action "clicked" item }}>{{item.title}}</span>
{{yield}}

And the tab-item class file to:

export default Ember.Component.extend({
  actions:{
    clicked: function(tabItem){
      this.sendAction("setSelectedTabItemAction", tabItem);
    }
  }
});

Here, you can see that we have added an action handler to deal with clicks on the tab-item title. This sends an action from the tab-item component to its parent, the tab-switcher component. The action bubbles up the hierarchy along with a parameter, namely the tabItem which we clicked on. This is so that it can be set as the current tab-item on the parent component.

Notice that we are using the property setSelectedTabItemAction as the action to send. This isn’t the actual action name that gets sent but the value contained in the property — in this case setSelectedTabItem, which is the handler on the parent component.

Conclusion

And that brings us to the end of this introduction to Ember components. I hope you enjoyed it. The productivity benefits of using reusable components throughout your Ember projects cannot be understated (and indeed throughout your projects in general). Why not give it a try? The source code for this tutorial is available on GitHub.

Are you using components in Ember already? What have been your experiences so far? I’d love to hear from you in the comments.

Frequently Asked Questions about Understanding Components in Ember 2

What is the main difference between Ember 2 and its previous versions in terms of components?

Ember 2 introduced a significant shift in the way components are handled compared to its previous versions. The most notable change is the introduction of the component lifecycle, which provides hooks that allow you to control when and how your component is rendered, updated, and destroyed. This gives developers more control and flexibility over their components. Additionally, Ember 2 promotes the use of components over views and controllers, which were more prevalent in previous versions.

How do I define a component in Ember 2?

Defining a component in Ember 2 is straightforward. You can create a component by running the command ember generate component component-name. This will create a JavaScript file and a Handlebars template file for your component. You can then define the component’s behavior in the JavaScript file and its layout in the Handlebars template.

What is the purpose of the yield keyword in Ember 2 components?

The yield keyword in Ember 2 components is used to insert the content of a block component. When you use a component in block form, you can provide content that is inserted into the component’s layout using the yield keyword. This allows you to create reusable components with customizable content.

How can I pass data to a component in Ember 2?

You can pass data to a component in Ember 2 by using attributes. When you use a component, you can specify attributes and their values. These attributes are then available in the component’s JavaScript file and Handlebars template. You can use these attributes to customize the behavior and layout of the component.

How can I handle user interactions in an Ember 2 component?

You can handle user interactions in an Ember 2 component by defining actions. Actions are functions that you define in the component’s JavaScript file. You can then use these actions in the component’s Handlebars template to respond to user interactions, such as clicks or form submissions.

How can I use services in an Ember 2 component?

Services in Ember 2 are singleton objects that can be accessed from any part of your application, including components. You can use services in a component by injecting them with the inject function. Once a service is injected, you can use it in the component’s JavaScript file and Handlebars template.

How can I test an Ember 2 component?

Ember 2 provides a testing framework that you can use to test your components. You can create a test for a component by running the command ember generate component-test component-name. This will create a test file where you can define tests for your component. You can then run your tests with the command ember test.

How can I use computed properties in an Ember 2 component?

Computed properties in Ember 2 are properties that automatically update when their dependencies change. You can define a computed property in a component’s JavaScript file using the computed function. You can then use this computed property in the component’s Handlebars template.

How can I use observers in an Ember 2 component?

Observers in Ember 2 are functions that are called when a property changes. You can define an observer in a component’s JavaScript file using the observer function. You can then use this observer to respond to changes in the component’s properties.

How can I use mixins in an Ember 2 component?

Mixins in Ember 2 are objects that contain methods and properties that can be shared among different parts of your application. You can use a mixin in a component by extending the component with the mixin using the extend function. Once a mixin is used, its methods and properties are available in the component.

Lamin SannehLamin Sanneh
View Author

An emberjs enthusiast, equally fascinated by laravel's clever but unorthodox use of fascades. Lamin Sanneh is a front end web developer who loves to dive into pretty much every thing web related. He teaches on youtube and you can also find him talking about web stuff here to anyone who would listen.

componentscontrollersjameshtemplatesviewsweb components
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form