11 min read

In this article by Suchit Puri, author of the book Ember.js Web Development with Ember CLI, we will focus on building reusable view components using Ember.js views and component classes.

(For more resources related to this topic, see here.)

In this article, we shall cover:

  • Introducing Ember views and components:
    • Custom tags with Ember.Component
    • Defining your own components
    • Passing data to your component
  • Providing custom HTML to your components
  • Extending Ember.Component:
    • Changing your component’s tag
    • Adding custom CSS classes to your component
    • Adding custom attributes to your component’s DOM element
  • Handling actions for your component
  • Mapping component actions to the rest of your application

Extending Ember.Component

Till now, we have been using Ember components in their default form. Ember.js lets you programmatically customize the component you are building by backing them with your own component JavaScript class.

Changing your component’s tag

One of the most common use case for backing your component with custom JavaScript code is to wrap your component in a tag, other than the default <div> tag.

When you include a component in your template, the component is by default rendered inside a div tag. For instance, we included the copyright footer component in our application template using {{copyright-footer}}. This resulted in the following HTML code:

<div id="ember391" class="ember-view">
<footer>
   <div>
       © 20014-2015 Ember.js Essentials by Packt Publishing
   </div>
   <div>

       Content is available under MIT license

   </div>

</footer>

</div>

The copyright footer component HTML enclosed within a <div> tag.

You can see that the copyright component’s content is enclosed inside a div that has an ID ember391 and class ember-view. This works for most of the cases, but sometimes you may want to change this behavior to enclose the component in the enclosing tag of your choice. To do that, let’s back our component with a matching component JavaScript class.

Let’s take an instance in which we need to wrap the text in a <p> tag, rather than a <div> tag for the about us page of our application.

All the components of the JavaScript classes go inside the app/components folder. The file name of the JavaScript component class should be the same as the file name of the component’s template that goes inside the app/templates/components/ folder.

For the above use case, first let’s create a component JavaScript class, whose contents should be wrapped inside a <p> tag. Let us create a new file inside the app/components folder named about-us-intro.js, with the following contents:

import Ember from 'ember';
export default Ember.Component.extend({
tagName: "p"
});

As you can see, we extended the Ember.Component class and overrode the tagName property to use a p tag instead of the div tag.

Now, let us create the template for this component. The Ember.js framework will look for the matching template for the above component at app/templates/components/about-us-intro.hbs.

As we are enclosing the contents of the about-us-intro component in the <p> tag, we can simply write the about us introduction in the template as follows:

This is the about us introduction.Everything that is present here   will be enclosed within a &lt;p&gt; tag.

We can now include the {{about-us-intro}} in our templates, and it will wrap the above text inside the <p> tag.

Now, if you visit the http://localhost:4200/about-us page, you should see the preceding text wrapped inside the <p> tag.

In the preceding example, we used a fixed tagName property in our component’s class. But, in reality, the tagName property of our component could be a computed property in your controller or model class that uses your own custom logic to derive the tagName of the component:

import Ember from "ember";
export default Ember.ObjectController.extend({
tagName: function(){
   //do some computation logic here
   return "p";
}.property()
});

Then, you can override the default tagName property, with your own computed tagName from the controller:

{{about-us-intro tagName=tagName}}

For very simple cases, you don’t even need to define your custom component’s JavaScript class. You can override the properties such as tagName and others of your component when you use the component tag:

{{about-us-intro tagName="p"}}

Here, since you did not create a custom component class, the Ember.js framework generates one for you in the background, and then overrides the tagName property to use p, instead of div.

Adding custom CSS classes to your component

Similar to the tagName property of your component, you can also add additional CSS classes and customize the attributes of your HTML tags by using custom component classes.

To provide static class names that should be applied to your components, you can override the classNames property of your component. The classNames property if of type array should be assigned properties accordingly. Let’s continue with the above example, and add two additional classes to our component:

import Ember from 'ember';
export default Ember.Component.extend({
   tagName: "p",
   classNames: ["intro","text"]
});

This will add two additional classes, intro and text, to the generated <p> tag.

If you want to bind your class names to other component properties, you can use the classNameBinding property of the component as follows:

export default Ember.Component.extend({
tagName: "p",
classNameBindings: ["intro","text"],
intro: "intro-css-class",
text: "text-css-class"
});

This will produce the following HTML for your component:

<p id="ember401" class="ember-view intro-css-class   text-css-class">This is the about us introduction.
Everything   that is present here will be enclosed within a &lt;p&gt;   tag.</p>

As you can see, the <p> tag now has additional intro-css-class and text-css-class classes added. The classNameBindings property of the component tells the framework to bind the class attribute of the HTML tag of the component with the provided properties of the component.

In case the property provided inside the classNameBindings returns a boolean value, the class names are computed differently. If the bound property returns a true boolean value, then the name of the property is used as the class name and is applied to the component. On the other hand, if the bound property returns to false, then no class is applied to the component.

Let us see this in an example:

import Ember from 'ember';
export default Ember.Component.extend({
tagName: "p",
classNames: ["static-class","another-static-class"],
classNameBindings: ["intro","text","trueClass","falseClass"],
intro: "intro-css-class",
text: "text-css-class",
trueClass: function(){
   //Do Some logic
   return true;
}.property(),
falseClass: false
});

Continuing with the above about-us-intro component, you can see that we have added two additional strings in the classNameBindings array, namely, trueClass and falseClass. Now, when the framework tries to bind the trueClass to the corresponding component’s property, it sees that the property is returning a boolean value and not a string, and then computes the class names accordingly.

The above component shall produce the following HTML content:

<p id="ember401" class="ember-view static-class   
another-static-class intro-css-class text-css-class true-class">
This is the about us introduction.Everything that is present   
here will be enclosed within a &lt;p&gt; tag.
</p>

Notice that in the given example, true-class was added instead of trueClass. The Ember.js framework is intelligent enough to understand the conventions used in CSS class names, and automatically converts our trueClass to a valid true-class.

Adding custom attributes to your component’s DOM element

Till now, we have seen how we can change the default tag and CSS classes for your component. Ember.js frameworks let you specify and customize HTML attributes for your component’s DOM (Document Object Model) element.

Many JavaScript libraries also use HTML attributes to provide additional details about the DOM element.

Ember.js framework provides us with attributeBindings to bind different HTML attributes with component’s properties. The attributeBindings which is similar to classNameBindings, is also of array type and works very similarly to it.

Let’s create a new component, called as {{ember-image}}, by creating a file at app/component/ember-image.js, and use attributes bindings to bind the src, width, and height attributes of the <img> tag.

import Ember from 'ember';
export default Ember.Component.extend({
tagName: "img",
attributeBindings: ["src","height","width"],
src: "http://emberjs.com/images/logos/ember-logo.png",
height:"80px",
width:"200px"
});

This will result in the following HTML:

<img
id="ember401"
class="ember-view"
src="http://emberjs.com/images/logos/ember-logo.png"
height="80px"
width="200px">

There could be cases in which you would want to use a different component’s property name and a different HTML attribute name. For those cases, you can use the following notation:

attributeBindings: ["componentProperty:HTML-DOM-property]
import Ember from 'ember';
export default Ember.Component.extend({
tagName: "img",
attributeBindings: ["componentProperty:HTML-DOM-property],
componentProperty: "value"
});

This will result in the the following HTML code:

<img id="ember402" HTML-DOM-property="value">

Handling actions for your component

Now that we have learned to create and customize Ember.js components, let’s see how we can make our components interactive and handle different user interactions with our component.

Components are unique in the way they handle user interactions or the action events that are defined in the templates. The only difference is that the events from a component’s template are sent directly to the component, and they don’t bubble up to controllers or routes. If the event that is emitted from a component’s template is not handled in Ember.Component instance, then that event will be ignored and will do nothing.

Let’s create a component that has a lot of text inside it, but the full text is only visible if you click on the Show More button:

For that, we will have to first create the component’s template. So let us create a new file, long-text.hbs, in the app/templates/components/ folder. The contents of the template should have a Show More and Show Less button, which show the full text and hide the additional text, respectively.

<p>
This is a long text and we intend to show only this much unless
the user presses the show more button below.
</p>
{{#if showMoreText}}
This is the remaining text that should be visible when we press
the show more button. Ideally this should contain a lot more
text, but for example's sake this should be enough.
<br>
<br>
<button {{action "toggleMore"}}> Show Less </button>
{{else}}
<button {{action "toggleMore"}}> Show More </button>
{{/if}}

As you can see, we use the {{action}} helper method in our component’s template to trigger actions on the component.

In order for the above template to work properly, we need to handle the toggleMore in our component class. So, let’s create long-text.js at app/components/ folder.

import Ember from 'ember';
export default Ember.Component.extend({
   showMoreText: false,
   actions:{
   toggleMore: function(){
       this.toggleProperty("showMoreText");
   }
   }
});

All action handlers should go inside the actions object, which is present in the component definition. As you can see, we have added a toggleMore action handler inside the actions object in the component’s definition. The toggleMore just toggles the boolean property showMoreText that we use in the template to show or hide text.

When the above component is included in about-us template, it should present a brief text, followed by the Show More button. When you click the Show More button, the rest of text appears and the Show Less button appears, which, when clicked on, should hide the text.

Building Reusable Components

The long-text component being used at the about-us page showing only limited text, followed by the Show More button

Building Reusable Components

Clicking Show More shows more text on the screen along with the Show Less button to rollback

Summary

In this article, we learned how easy it is to define your own components and use them in your templates. We then delved into the detail of ember components, and learned how we can pass in data from our template’s context to our component. This was followed by how can we programmatically extend the Ember.Component class, and customize our component’s attributes, including the tag type, HTML attributes, and CSS classes. Finally, we learned how we send the component’s actions to respective controllers.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here