Ember.js provides us with two types of controllers: Ember.ObjectController
and Ember.ArrayController
. We have been using Ember.ObjectController
in many examples throughout this book. ObjectController
works on one object and ArrayController
works on an array or list of objects.
When we say that
ObjectController
works on a single object, we mean that the model property of the corresponding route returns the single JavaScript object and not an array. You can also use the setupController
hook that was discussed in the previous chapter to set the model property of the controller. The default behavior of the setupController
method is to set the model property on the controller to what is returned from the model method present in the route. So, you can either set the model in setupController
or return the object from the model method present in the route.
ArrayController
, on the other hand, has been designed to work on a list of items and hence the model, or the setupController
hook, should set the model to an array of objects.
Let's see how can we use the ArrayController
to display a list of products that is received from the server or similar.
The first thing we would need to do is to return an array of products from the model property of our corresponding route. Here, in our example, we will be using the products route to show the list of products:
import Ember from "ember"; export default Ember.Route.extend({ model: function(){ return[ { "name":"Leather Jacket", "description":"A very long jacket description which cannot be shown inside a table and will have to be shortened", "currency":"USD", "symbol":"$", "price":"1999.999", "dimensions":{ "length":7.0, "width":12.0, "height":9.5 } }, { "name":"Some Coat", "description":"A very long coat description which cannot be shown inside a table and will have to be shortened", "currency":"EURO", "symbol":"€", "price":"299.999", "dimensions":{ "length":8.0, "width":11.0, "height":10.0 } }, { "name":"T Shirt", "description":"A very long T Shirt description which cannot be shown inside a table and will have to be shortened", "currency":"USD", "symbol":"$", "price":"58.999", "dimensions":{ "length":9.0, "width":13.0, "height":11.0 } }, { "name":"White Shirt", "description":"A very long Shirt description which cannot be shown inside a table and will have to be shortened", "currency":"GBP", "symbol":"£", "price":"1999.9999", "dimensions":{ "length":10.0, "width":14.0, "height":12.0 } } ] } });
The products route is present at app/routes/products.js
Here, we returned a static list of products from the model
property of our route, but in most real-world cases, this would be fetched from the backend server. Here, each of our products has a name
, description
, currency
, symbol
, price
, and dimensions
property. The only issue is that the price
is a floating point number and not a formatted number, so we will have to format the price and show it properly with the currency symbol. Also, the description that comes from the backend server is too long to be displayed in a table, so we will have to truncate that, as well.
Since most of the changes discussed above are related to the UI of the table, putting the functionality in the controller
seems an obvious choice.
Let's look at the products
controller:
import Ember from "ember"; export default Ember.ArrayController.extend({ sortProperties: ['name'], sortAscending: true, itemController: 'product' });
The products controller is present at app/controllers/products.js
As the products controller needs to work on a list of products, it should extend Ember.ArrayController
. Ember.ArrayController
includes the Ember.SortableMixin
that implements the sorting of the items present in the model property of the controller. Ember.SortableMixin
uses two properties to sort the underlying elements.
The first one is sortProperties
, which contains an array of properties on which the items should be sorted. In our case, we sort the items in the table according to their names. The second property read by Ember.SortableMixin
is sortAscending
, which is a boolean variable which tells whether to use ascending order or descending order while sorting the elements.
Now, since ArrayController
works on a complete array and not on individual items, the functionality to format date, description, and dimensions can't go in here. The products controller will contain properties that work on the array as a whole; for example, if we had to show how many products are above $50, we would add the computed property in the products controller.
For an individual item-specific transformation, the Ember.ArrayController
can contain an itemController
property, which should point to the controller that needs to be used for every item in the list. As you can see in the preceding code sample, we point itemController
to the product
. This tells the Ember.js framework to use product
controller to decorate each of the item present in the model
array.
Let's look at app/controllers/products/product.js
, that is, the product
controller that contains the computed properties decorating the original properties received from the severof
individual items present in the model
array:
import Ember from "ember"; export default Ember.ObjectController.extend({ formattedPrice: function(){ returnthis.get('symbol') + " " + $.number(this.get('price'),2); }.property('symbol','price'), formattedDimension: function(){ returnthis.get('dimensions.width') + " x " + this.get('dimensions.height') + " x " + this.get('dimensions.length'), }.property('dimensions.width','dimensions.height','dimensions.length'), shortDescription: function(){ varshortDesc = this.get('description').substring(0, 25); returnshortDesc + "..."; }.property('description') });
Product controller is present at app/controllers/products/product.js
The product
controller defined above extends Ember.ObjectController
and works on an individual item of the array as its model property. Here, you can see that we have defined three computed properties: formattedPrice
, formattedDimension
, and shortDescription
. These computed properties transform the data and make it presentable to the end user.
Now, since we have defined all the controllers and routes, the only thing left is to display the list of items in the products
template. So, let's create the products
template and present the products data in a tabular form, as shown in the following:
<h1> Products </h1> <table class="table table-bordered"> <thead> <tr> <th data-field="name">Item</th> <th data-field="description">Description</th> <th data-field="dimension">Dimension</th> <th data-field="price">Price</th> </tr> {{#each product in controller}} <tr> <td>{{product.name}}</td> <td>{{product.shortDescription}}</td> <td>{{product.formattedDimension}}</td> <td>{{product.formattedPrice}}</td> {{/each}} <tr> </thead> </table>
The products template is present at app/templates/products.hbs
In the products.hbs
template, we iterate over every item present in the products controller and present the transformed data from the product controller. itemController
enables us to use {{product.shortDescription}}
without much of configuration. This keeps the code clean and easy to understand.
If you go inside the
chapter5/example1
directory and run ember serve
, you will see the following output at http://localhost:4200
:
18.225.235.144