Defining templates

Let's see how templates fit in to our Ember.js application. When you create a new application using Ember CLI, you will find the app/templates folder inside your application. This is where the Ember.js resolver looks for template files for your project.

Let's look at the default application.hbs template present at app/templates/:

<h2 id="title">Welcome to Ember.js</h2>
{{outlet}}

The default application template is present at chapter-3/example1/app/templates/application.hbs

The application template is the main template that is rendered when the application starts. The most common use case for application templates is to put in the header and footer of the application here. This is the entry point for the application and it makes sense to put in sections of the application that will remain visible throughout the application here. All the routes that are rendered will be nested under the application route. We will talk more about the routes and naming conventions used to associate templates with model, views, and controllers in Chapter 4, Managing Application State Using Ember.js Routes.

The application template should also contain at least one {{outlet}} helper. The outlet helper tells the router to render the next appropriate template in place of the {{outlet}} helper. The next template is usually resolved from the routes and URL convention. We will be discussing more about the convention router in Chapter 4, Managing Application State Using Ember.js Routes.

Let's change the application template to include our headers and footer notes. Let's also render the following templates between header and footer by placing the {{outlet}} helper in between the header and footer tags:

<header>
  <h2>Welcome to Ember.js</h2>
</header>
<div>
  {{outlet}}
</div>
<footer>
  &copy;2014 Ember.js Essentials
</footer>

The modified application.hbs that now includes header and footer information is present at chapter-3/example1/app/templates/application.hbs

As you can see, here we have moved the outlet tag between the header and footer. Let's create a new index template that now renders between the header and footer when we access the http://localhost:4200/ URL. We will be discussing more about how the routes match and resolve the template names in Chapter 4, Managing Application State Using Ember.js Routes. Till then, it is safe to assume that when you open the application index URL in the browser, the application template is rendered, which, in turn, renders the index template in place of the {{outlet}} helper.

To create the index template, let's create a new file index.hbs inside the app/templates/ directory with the following content:

<ul>
  {{#each item in model}}
    <li>{{item}}</li>
  {{/each}}
</ul>

The contents of index template are present in chapter-3/example1/app/templates/index.hbs

Now, if you run chapter-3/example1 by running the ember serve command in the terminal and open the http://localhost:4200/ URL in your browser, you will see the following output:

Defining templates

Adding a header and footer in the application template

Here, we have used a simple header and footer, but you could potentially use a more complex and better UI for your application.

Your template can also have more than one outlet. In such cases, outlets are usually named so that you can tell the router which outlet to render your template, as follows:

{{ outlet "sidebar"}}

We will be discussing more about named outlets when we discuss routes in Chapter 4, Managing Application State Using Ember.js Routes.

Handlebars.js expressions

By this time, you would have figured out that Handlebars.js expressions are wrapped in {{}}. The Ember.js framework provides us with two initial routes: the application route and index route. The application route renders the application template present at app/templates/application.hbs when the application initializes. The index route is activated when the users visits the / or the index of the application. It renders the index template from app/templates/index.hbs. If you need any custom behavior other than what's already there, you will have to provide your implementation of the routes. Template will look for data from its associated controller. "Convention Over Configuration" governs the association between a controller and template. You don't have to manually wire up things here.

In our case, the index template will look for data from the index controller present and exported from app/controllers/index.js. But we have not defined the index controller anywhere. Whenever the Ember.js framework cannot find the required component, it will try and generate one for you. In our case, even though we have not defined the index controller, the framework will not complain and generate one for you. This generated component will have the default behavior that basically does nothing. This makes our code clean and saves us from writing components that do nothing.

Let's look at the index template present at chapter-3/example1/app/templates/index.hbs:

<ul>
  {{#each item in model}}
  <li>{{item}}</li>
  {{/each}}
</ul>

You can see here that we are iterating items present in model array. Now, as we discussed above, the template will look for controller to provide the data. Now, since the "model" attribute is not present in the default index controller implementation, it should be set externally from somewhere else.

Let's look at the index route present at chapter-3/example1/app/routes/index.js:

import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    return ['red', 'yellow', 'blue'];
  }
});

You can see here that we are returning an array containing three colors, ['red', 'yellow', 'blue'], from the model method. Doing this, the framework will automatically set the model property on the corresponding index controller to ['red', 'yellow', 'blue'].

That's why we do the following:

{{#each item in model}}
<li>{{item}}</li>
{{/each}}

In the index template, the model property is fetched from the index controller that is set from the index route by the framework.

Normally, a model method would return the data that was fetched from the server and the properties that don't have to be persisted to the server and are part of the controller definition.

Here, if we wanted to access some properties that are part of the controller, we will be accessing them directly by their name in the template. Let's see this in an example.

Now, since we want to access custom properties from the controller, we will have to define them first, as shown in the following:

import Ember from 'ember';

export default Ember.Controller.extend({
  name: "Suchit Puri"
});

The index controller present at chapter-3/example1/app/controller/index.js

Here, we have defined a name property in the index controller of our application; let's change the template to use this property, as shown in the following:

Hi, this is {{name}}.
  I like the following colors.
<ul>
    {{#each item in model}}
<li>{{item}}</li>
    {{/each}}
</ul>
</script>

The index template of our application using properties from the controller, the index template can be found at chapter-3/example1/app/templates/index.hbs

Here, you can see that when we use {{name}} in the template, it will look for that property in the index controller, which would return the correct name. Now, the name property present in the index template is bound to the template, which means that if you change the name property of index controller, the change will automatically be reflected in the template.

Now, since the basics for Handlebars.js expressions are clear, let's jump into the detailed syntax of the Handlebars.js template and see how easy it is to create custom templates for your application.

Handlebars.js conditionals

Handlebars.js does not promote the use of complex business logic inside your templates. It makes it difficult to mix complex business logic in the templates by providing a very limited set of helper and scope methods. This makes your templates very clean and decoupled from the business logic.

Using complex business logic in your templates makes them very difficult to understand and debug. In any case, writing business logic in templates is a bad design choice and violates the separation of concerns (SoC) design principle, which states: "In computer science, separation of concerns (SoC) is a design principle for separating a computer program into distinct sections, such that each section addresses a separate concern."

Let's look at the if, else, and unless conditionals in Handlebars.js.

If, else, and unless

Frequently you will run into situations where in you would want to show or hide part of the template based on some condition that returns a boolean result. Handlebars.js conditionals are made exactly for the same purpose. The if conditional is a Handlebars.js helper method, which will execute the code block enclosing it when the condition is true, else will render the contents of the else block. Let's see this by looking at the following example:

{{#if edit}}
<ul>
  {{#each item in model}}
  <li>{{item}}</li>
  {{/each}}
</ul>
{{/if}}

<button {{action 'changeEdit'}}>Toggle</button>

The index template is present at chapter-3/example2/app/templates/index.hbs

As you can see in the preceding code, we have moved the earlier content of the index template of example1 inside the if condition. We have also added a toggle button that will toggle the property of the edit flag when the button is clicked. We are using the {{action}} helper method to trigger an event on the click of the button. We will be talking more about the {{action}} helper later in this chapter, but till then it's safe to assume that on clicking the button will trigger an action changeEdit on the index controller.

Let's look at index controller next; as we discussed in the previous sections, a controller backs the respective template to supply the bound properties. So, in order to support the preceding index template, we will have to define the actions and properties inside the index controller that can then be used inside the template, as shown in the following:

import Ember from 'ember';

export default Ember.ObjectController.extend({
  edit: true,
  actions:{
    changeEdit: function(){
      this.toggleProperty('edit'),
    }
  }
});

The index controller is present at chapter-2/example2/app/controller/index.js

As you can see, the index controller has an edit property set to true. It also has an actions object that contains the implementation of our custom actions. This implementation is called whenever the user uses the {{action}} helper in the template.

So, whenever the user clicks on the Toggle button, it triggers the changeEdit action event, and this event triggers the changeEdit method in the actions object that toggles the edit property of the controller. Now, since the template is bound to the edit property of the controller, it re-evaluates itself and show/hides the HTML present inside the {{#if}} {{/if}} block.

An if block can conditionally have an {{else}} block, which will execute the block when the if block evaluates to false, like any other if-else block:

{{#if edit}}
<!—do something here -->
{{else}}
<!—do something else here -->
{{/if}}

The Ember.js framework also gives you an easy syntax to check for a negative of a boolean value; so, for example, if you wanted to check if some boolean value is not true, then only execute a block, which you could do by using the {{#unless}} {{/unless}} block. The unless helper behaves exactly similar to the {{#if}} {{/if}} helper methods, the only difference being that it checks for the negation of the boolean value instead of the boolean value.

Note

One interesting thing to note here is that {{#if}} and {{#unless}} are examples of block expressions, which means that these helper methods allow you to execute your helper method on a portion of template. Such helper methods in the Ember.js template system begin with # and require a closing expression to signify an end.

Displaying a list of items using Handlebars.js

One of the common use cases in today's modern web applications is showing a list of data in a tabular form. Handlebars.js allows you to easily do that via the {{#each}} helper.

By now, you might have observed that index template in chapter-3/example1/app/templates/index.hbs is a classic example of iterating a list of elements. Let's see this again in the following:

<ul>
    {{#each item in model}}
<li>{{item}}</li>
    {{/each}}
</ul>

In the preceding example you can see that we are iterating over an array called as model. This model is set in index route, as shown in the following:

import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    return ['red', 'yellow', 'blue'];
  }
});

When we say {{#each item in model}}, we are looping over all the elements present in the array model, and, in each iteration, we assign the current object to item. In our case, the model array contains just string names, but in real scenarios, the model can be much more complex and can contain complex JavaScript objects. Let's see this by an example: first, we need to change the index route to return a more complex object. So, instead of color names, let's return an array containing company information objects, as shown in the following:

import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    var companies = [{
      "name" : "Google",
      "headquarters": "Mountain View, California, United States of America",
      "revenue":"59825000000"
    },{
      "name" : "Facebook",
      "headquarters":"Menlo Park, California, United States of America",
      "revenue":"7870000000"
    },{
      "name" : "twitter",
      "revenue": "664000000",
      "headquarters":"San Francisco, California, United States of America"
    }];
    return companies;
  }
});

The index route is present at chapter-3/example3/app/routes/index.js

Here we return an array of companies from our model function in index route, every company object is of the form:

{"name" : <<company name>>,"headquarters":<<headquarters>>,
"revenue":<<revenue>> }

Now let's create the index template to display the list of companies in a tabular form, as shown in the following:

<table id="t01">
  <tr>
    <th>Company Name</th>
    <th>Headquarters</th>
    <th>revenue</th>
  </tr>
  {{#each item in model}}
  <tr>
    <td>{{item.name}}</td>
    <td>{{item.headquarters}}</td>
    <td>{{item.revenue}}</td>
  </tr>
  {{/each}}
</table>

The index template is present at chapter-3/example3/app/templates/index.hbs

Here, we define a table in plain HTML markup language with our headings as Company Name, Headquarters, and revenue. Since each row in our table should display the respective company information, we will have to iterate our model array and for each record, we will have to create a new row in our table with three columns having respective data items.

Doing this will produce a table similar to that shown in the following image:

Displaying a list of items using Handlebars.js

Iterating a complex model object

As you saw in the above example, it is easy to display a list of objects in a tabular form. You can see that the display logic is completely unaware of how to fetch the data. Like in our case, we have hardcoded the list of company information, but in a real scenario, you would be fetching the company list from a backend server. Doing this would only affect the implementation of index route's model method and everything else remains exactly the same.

The other benefit you get out of this approach is that the template is bound to the items present in the model array, which means that when you add or remove a company item from the model array, the view will render the company list accordingly.

Binding HTML tag attributes

In the last section, we saw how to bind values from the model object within an HTML tag. But sometimes, you may want to bind the attributes, instead of value, of an HTML tag. For example, you may want to bind the class attribute of a <div> tag because you want to style elements differently based on some logic that is accessible by the controller.

Let's see that by an example, example4. We will use the previous example chapter-3/example3 as our base.

Assuming that we have a requirement to show the headquarters column text color based on some property in the controller. Handlebars.js allows you to do use the {{bind-attr}} helper method to solve such situation. The {{bind-attr}} method will bind the attribute name given next to a property, accessible by the controller or view:

<table id="t01">
  <tr>
    <th>Company Name</th>
    <th>Headquarters</th>
    <th>revenue</th>
  </tr>
  {{#each item in model}}
  <tr>
    <td>{{item.name}}</td>
    <td {{bind-attr class="className"}}>{{item.headquarters}}</td>
    <td>{{item.revenue}}</td>
  </tr>
  {{/each}}
</table>
<button {{action "toggleColor"}}> Change color </button>

The index template is present at chapter-3/example4/app/templates/index.hbs

As you can see in the preceding code, the only change from the previous example is that we have used the {{bind-attr class="className"}} helper to bind the class of headquarters column. The {{bind-attr class="className"}} helper, will look for className property in the controller to resolve the class name.

We have also added a new button at the bottom to trigger an action called toggleColor in the controller, whose responsibility will be to change the className property of the controller, based on some logic.

Till now, we have been using the default index controller, which is generated by the Ember.js framework in our examples. But now, as we need custom properties and action in our controller, we will have to explicitly define it:

import Ember from 'ember';

export default Ember.ObjectController.extend({
  className:"red",
  actions:{
    toggleColor: function(){
      if(this.get("className") == "red"){
        this.set("className","blue");
      }else{
        this.set("className","red");
      }
    }
  }
});

The index controller is present at chapter-3/example4/controllers/index.hbs

Here, you can see that we have defined a property called className, which will be bound to the class attribute of the headquarters column. We have also defined a new action, called toggleColor, which changes the className property from red to blue and vice versa.

The only thing left in our example is to define the two CSS classes, red and blue, which will set the color attribute to their respective colors. The CSS classes should be added to chapter-3/example4/app/styles/app.css file, as follows:

.red {
color: red;
}
.blue {
color: blue
}

Now, when you run the above example, you will see that the headquarters column now is rendered in red color. If you press the change color button, you will notice that the headquarters color changes to blue. On subsequent presses, you will notice that the color toggles between red and blue.

The above example works because the {{bind-attr class="className"}} helper binds the controller property className with the class attribute of the <td> tag. So, whenever the className property of the controller changes, that change is automatically propagated to the class attribute of the <td> tag.

You might be thinking, why can't you just use <td class={{controller.className}}> or something similar, instead of using the {{bind-attr}} Handlebars.js helper method? The answer to the above question lies in the understanding how Ember.js tracks which section of the HTML page is to be updated when the corresponding bound property changes.

Like in our case, the Ember.js framework will have to keep track of which section of my HTML page needs to be updated when someone changes the companies array returned from model method present inside the index controller.

If you see the generated HTML of the company's table, you will find that apart from the regular <table><tr><td> tags, there are some additional attributes present in the <td> tags, something like the following:

<td class="red" data-bindattr-258="258">Mountain View, California, United States of America</td>

These data bind attributes help the Ember.js framework to track which attributes to update when the corresponding bound properties in the controller changes.

Apart from data-bindattr, the Ember.js framework also inserts special <script> tags, like the following:

<script id="metamorph-0-start" type="text/x-placeholder"></script>
<script id="metamorph-0-end" type="text/x-placeholder"></script>.
To track the sections of HTML page it should update when the corresponding properties changes. So as a result when you do
<td class={{item.name}}>

It leads to something like the following:

<td class="<script id="metamorph-0-start" type="text/x-placeholder"></script>Google
<script id="metamorph-0-end" type="text/x-placeholder"></script>"

This leads to HTML that is invalid and leads to an error in our code as the value of the class attribute is invalid. This is the reason we need to use special Handlebars.js helpers instead of just using <td class={{item.name}} >.

Ember.js team has also extracted the preceding functionality into a separate library that is called as Metamorph.js. It can be found on GitHub at https://github.com/tomhuda/metamorph.js/. This is particularly helpful for people who are writing their own frameworks and want to use Metamorph.js to know which sections of the page to change when their corresponding JavaScript properties changes.

Ember.js provides another workaround the above problem by using the {{unbound}} helper. We saw that the problem arises when the framework needs to bind the UI to JavaScript properties so that if the properties change the UI is updated accordingly. What the unbound helper does not bind the JavaScript properties to the UI and hence works fine even with the attributes; the only caveat there is that the properties are not bound to the UI and hence the UI is not updated even if the JavaScript property changes.

Action event bubbling

Handling user interaction is the core and the most important part of any modern web application. Showing, hiding, and deleting information, based on the user's interaction, is common to most web applications today.

We did the same thing in the Company brochure example, where we changed the color of the headquarters text, based on the click of a button. Though we have just changed the color of the text, but the possibilities are endless. You can show or hide elements at the click of the button, or change the full theme of the application by using the actions helper.

You may have noticed that the index template where we created a button Change Color triggers the toggleColor action on the controller:

<button {{action "toggleColor"}}> Change color </button>

You can see here that we use the {{action}} Handlebars.js helper method to trigger the action on the click of the button. Action helper can be used to make the HTML tags clickable.

When the user clicks on the element where the {{action}} helper has been used, the helper triggers an event to the controller, like in case of chapter-3/example4, the action triggers the toggleColor event to the index controller. The controller needs to define a method with the same name as the event in its action object:

import Ember from 'ember';
export default Ember.ObjectController.extend({
  className:"red",
  actions:{
    toggleColor: function(){
      if(this.get("className") == "red"){
        this.set("className","blue");
      }else{
        this.set("className","red");
      }
    }
  }
});

The index controller is present at chapter-3/example4/controllers/index.hbs

Here you can see that we have defined an actions object inside the index controller. The toggle method resides inside this actions object.

Note

One important thing to note here is that the component (controller or route) that handles the event triggered by the action helper should define the event handler function inside the actions object. The event will not be triggered if you define the method outside the action object.

Once the event is triggered from the DOM element, by default the event will go to the controller that is backing the template. If the controller does not implement the event handler method in its action object, the event will go to the route associated with the controller. If the route also does not implement the event handler method in its action object, it will go to the parent route and so on, till it reaches the application route.

Action event bubbling

Event action bubbling

This behavior gives us the ability to handle events at different levels in our application. For example, if there are some application-wide alerts or an error message box that needs to be shown, placing the error event handler in the application route's action object will be enough to show the error message application-wide from one place.

Let us build an application-wide event handler that will show alert notifications throughout the application.

We will start with the templates first in our chapter-3/example5, starting with the application template. The example5 is an extension from example4:

<div {{bind-attr class="className"}}>{{message}}</div>
<h2 {{action "alert" "something went wrong form the Application Template"}}>Company Information Brochure</h2>
    {{outlet}}

The application template is present at chapter-3/example5/app/templates/application.hbs

In the following, we have added a new <div> element to house the alert:

<div {{bind-attr class="className"}}>{{message}}</div>

Here, we are using {{bind-attr}} helper to bind the class name of the div tag. The contents of the div tag will be provided by the {{message}} property from the application controller. By default, the div will be hidden, and when there is some action triggered, we will make this div visible.

Take the following line:

<h2 {{action "alert" "something went wrong form the Application Template"}}>Company Information Brochure</h2>

We are using the action helper to trigger an action alert on the click of the <h2> tag. We are also passing the message to be shown as an argument to the alert method.

Let's look at the application controller; the controller needs to provide the data needed by the preceding application template:

import Ember from 'ember';
export default Ember.ObjectController.extend({
className:"hide",
message: ""
});

The application controller is present at chapter-3/example5/app/controllers/application.js

Since the application template uses the className and message properties in the template, we need to serve them from the controller. So, we have set the className property to hide—this CSS class will hide the alert div. This is done so that initially nothing is visible to the user. The message property is set to an empty string, as initially there will not be any messages to show in the alert div.

Now, since we have used the {{action}} helper to trigger the alert event, we need to define an event handler for the same in either application controller or application route. As we can see that the application controller does not define any actions object, the event is bubbled up to the application route:

import Ember from 'ember';

export default Ember.Route.extend({
  actions:{
    alert: function(message){
      varapplicationController = this.controllerFor("application");
      applicationController.set("className","alert");
      applicationController.set("message",message);
      Ember.run.later(function(){
        applicationController.set("className","hide");
      },2000);
    }
  }
});

The application route is present at chapter-3/example5/app/routes/application.js

We need to define the alert event handler in the actions object of the application route so that when the event bubbles up from the controller to the route, it will be handled in the route.

In the alert event handler, we set the className property of the application controller to alert so that it becomes visible from hidden. We also set the message that we passed through the {{action}} helper on the controller.

The Ember.run.later method is an Ember.js equivalent of the setTimeout method. It respects the Ember.js Run Loop. We will talk more about Ember.js Run Loop in the upcoming chapters.

Now you should be able to understand from the code that we show the alert for 2 seconds, and after that we set the CSS class of the alert to hide, which will hide the alert <div> element from the web page.

The effect of doing this is that when you click on the heading Company Information Brochure, you should see an alert on the top of the page that disappears after 2 seconds, as shown in the following screenshot:

Action event bubbling

Application-wide alert box

Till now, we have created an alert that gets triggered on the click of a heading tag that is present in the application template. Normally, alerts like these will be triggered after performing some business logic that results in an error. This business logic can be placed in the alert event handler of the application.

Next, we would want to utilize the event bubbling capability of the framework to trigger this action from anywhere in the application. Let's change the index template to now trigger the alert action when anyone clicks on the different columns of the table, as shown in the following:

<table id="t01">
  <tr>
    <th>Company Name</th>
    <th>Headquarters</th>
    <th>revenue</th>
  </tr>
  {{#each item in model}}
  <tr>
    <td {{action "alert" "alert form company name" }}>{{item.name}}</td>
    <td {{action "alert" "alert form company headquarters" }}>{{item.headquarters}}</td>
    <td {{action "alert" "alert form revenue" }}>{{item.revenue}}</td>
  </tr>
  {{/each}}
</table>

The index template of chapter-5/example5 is present at chapter-5/example5/app/templates/index.hbs

As you can see, the template remains the same, with just one exception. We have now added the {{action}} helper in the <td> tag of the table. With our design, we now have the ability to pass in different messages from different parts of the application. This results in alert event being triggered on the click of different columns of the table. Doing this has enabled us to trigger application alert throughout the application in a consistent way.

What if we want to handle the events trigger by the index template in a different way? For this, we just need to define the index controller and write our business logic in the alert event handler of the controller, as shown in the following:

import Ember from 'ember';

export default Ember.ObjectController.extend({
  actions:{
    alert: function(){
      //do some controller level processing
      return true;
    }
  }
});

The index controller of chapter-5/example5 is present at chapter-5/example5/app/templates/index.js

Now, when we click on any of the columns of the table present in the index template, we can perform some logic there that is very specific to the index controller.

One very important thing to note here is the return true; statement at the end of the alert function. If the event handler returns true, it tells the framework to continue with the event bubbling and the event continues its propagation till the application route. If the alert event handler returns false, this would stop the event propagation and should be used in scenarios where different sections of the page have different alerts requirements.

Note

Till now, all of our events got triggered on the mouse click. If you want to change this, you can use the on attribute of the actions helper to trigger the event on mouse up or mouse down, or so on:

{{action "alert" "message" on="mouseUp"}}

Handlebars.js input helpers

Ember.js provides us with some useful input helpers to manage text boxes, text areas, checkboxes, and select box.

You could do something like the following:

{{input type="text" value=firstName disabled=nameDisabled size="40"}}

This will render an HTML input box, whose value is bound to the firstName property in the controller. Similarly, the disabled property of the text box is bound to the nameDisabled property of the corresponding controller:

<div>Hi Mr. {{firstName}} {{lastName}}</div><br>
<div> Last Name is disabled: {{nameDisabled}}<br></div>
<br>
<div>First Name : {{input value=firstName size=20 }}</div>
<div> Last Name : {{input value=lastName disabled=nameDisabled}}</div>
<br><br>
<div>Enable last name ? {{input type="checkbox" checked=nameDisabled}}</div>

The index template of chapter-3/example6 is present at chapter-3/example6/app/templates/index.hbs

import Ember from 'ember';

export default Ember.ObjectController.extend({
  firstName: "",
  lastName: "Puri",
  nameDisabled: true
});

The index controller of chapter-3/example6 is present at chapter-3/example6/app/controller/index.js

In the preceding example, we have two input types: a text box to accept the first name and the last name, and a checkbox to enable/disable the last name textbox.

You can see in the previous example that the checked attribute of the checkbox and the disabled attribute of last name input box are bound to the same controller property disableName, which is by default set to true. Doing this would enable or disable the last name text box on checking, unchecking the disable last name? check box.

Similar to the text box, the text area input helper is self-explanatory and renders an HTML text area:

{{textarea value=longText cols="50" rows="4"}}

This would render a text area, whose value is bound to the longText property of the controller and has 50 columns and 4 rows.

Building custom Handlebars.js helpers

As seen in the earlier sections, Ember.js provides us with helper methods that enable us to do most common tasks very easily and quickly. But very soon we will end up in situations wherein we start duplicating tasks that were not possible with the default helpers. For example, we want to truncate text to be shown on the page to, say, 10 characters. One way to go about solving this problem would be to add a computed property in our controller, which binds to the long text property and returns the truncated text. This solution is not reusable as it is limited to one controller.

What if we could create our own helper tags that could be used application wide? The Ember.js framework lets you do that very easily. Let's see that by creating a custom truncate helper that will truncate the text passed to it, we would also need to pass in the length after which we truncate the text.

Helpers in Ember CLI projects go inside the app/helpers directory. The following truncate helper will go inside app/helpers/truncate.js:

import Ember from "ember";

export default function(value, options) {
  var length = 40;
  if(!Ember.isEmpty(options.hash.length)){
    length = options.hash.length;
  }
  if(!Ember.isEmpty(value)){
    if(value.length < length) {
      return value;
    }
    return value.substring(0, length) + "...";
  }
  return "";
};

The truncate helper is present at chapter-3/example7/app/helpers/truncate.js

You can see from the preceding code that we have defined a function that takes in a value and options arguments to handle the truncate helper. The helper method will truncate the text and will also respect any length attribute that is passed as an argument to the helper.

Ember CLI has two formats of writing the helper; these formats depend on the name of the helper method. Ember CLI encourages the helper names to contain a -. This helps disambiguate properties from helpers and improves the resolution performance of the framework as it is confident that the names containing - will be helpers. If the name of the helper contains a -, then the helper is resolved and will be registered automatically.

So, the above helper could be written as follows:

// app/helpers/trun-cate.js
import Ember from "ember";

export default Ember.Handlebars.makeBoundHelper(function(value, options) {
  // The same logic goes in here
});

Please note that if the name contains -, we export Ember.Handlebars.makeBoundHelper, instead of just the function. Doing this makes and registers the helper in Ember.js in one go.

When the name of the helper does not contain -, we will have to explicitly register the helper in app/app.js using Ember.Handlebars.registerBoundHelper.

import truncateHelper from './helpers/truncate';
Ember.Handlebars.registerBoundHelper('truncate', truncateHelper);

Here, as you can see, we are using the framework's Ember.Handlebars.registerBoundHelper to register our helper method. Ember.Handlebars.registerBoundHelper takes in two arguments: the helper name and a function that will be called when the helper is used.

Now let's see how we can use the helper method that we registered with our application:

{{truncate "This is very very long and beyond" length=20}}
<br>
{{truncate "this is very long" length=10}}

The truncate method being used in the index template is present at chapter-3/example7/app/templates/index.hbs

<h2>{{truncate "Welcome to Ember.js" length=10}}</h2>
{{outlet}}

The truncate helper being used in the application template is present at chapter-3/example7/app/templates/application.hbs

You can see that we can use our truncate Handlebars.js helper across the application, like any other helper method. We can also pass in the length attribute to make it more flexible.

If you run the above chapter-3/example7 using ember serve and navigate to http://locahost:4200, you will see the truncated text, as shown in the following:

Building custom Handlebars.js helpers

Using our truncate helper to truncate the text entered in the text box

Like our truncate helper, you can create other helpers to follow a set of style guidelines across your application. The benefit of this approach is very open-ended and depends on your imagination to bring consistency across your application.

Using the concise Emblem.js templating language

For some users, Handlebars.js may appear to be a very verbose template language as it uses plain HTML whereas users would prefer to use a more concise indentation-based language, something similar to Slim, Jade, Haml, and so on.

Thanks go to Alex Matchneer for creating Emblem.js, which is an indentation-based templating library like Slim, Jade, and Haml. So, if you have worked on any of these libraries before, you should be able to catch up with Emblem.js pretty quickly.

Emblem.js compiles to Handlebars.js and is fully compatible with the built-in Handlebars.js helpers that we have discussed in this chapter.

Installation

To use Emblem.js, you need to run the following command in your Ember application directory, which in our case is chapter-3/example8/:

ember install:addon ember-cli-emblem-hbs-printer
emberinstall:npm emblem

This will install the add-on emblem compiler that is compatible with the Ember CLI asset pipeline. More information about the Ember emblem add-on can be found at: https://github.com/201-created/ember-cli-emblem-hbs-printer.

Using Emblem.js

Creating an Emblem.js template is very similar to what we have been doing till now with Handlebars.js, except for the change in the extension of the file. The template files now end with .embl or .emblem, instead of .hbs:

h2 Welcome to Ember.js Application, it uses Emblem.js, the concise indentation based templating library
  = outlet

The application emblem template is present at chapter-3/example8/app/templates/application.emblem

h3 From Index Template
  ul
    each item in model
    li=item

The index emblem template is present at chapter-3/example8/app/templates/index.emblem

You can see in the above that instead of using plain HTML, we use a different syntax, which is based on indentation. All the elements are rendered based on how you arrange different elements of your template via spaces.

You can see that h3 and the ul tag are at the same level. Similarly, the each loop is nested inside the ul tag. The li tag is nested within the each loop block. We use two spaces to nest an element inside its parent element.

The benefit of indentation-based templates is that they are very concise and readable, as you don't have to search for closing HTML tags as you do in plain HTML.

This section gives you a very brief introduction to Emblem.js. We will not be covering the syntax and detailed working of Emblem.js in this book, and it is left to the reader to refer to http://emblemjs.com/ and https://github.com/machty/emblem.js for more information about the framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.198.94