Menu

Getting Started with Ember

26 December 2016

Goal: Build an app that will record a list of todos.
Time: 2 hours (let me know if it's very different for you)
Last Update: 7th September 2015.

Introduction

I'm writing the tutorial that I wish I had 24 months ago when I was starting out with Ember. The tutorial that doesn't assume that I've been using jQuery for 5 years, know what state is, eat 'promises' for breakfast or even understand the term 'hook'. It's a tutorial for beginners, beginners to coding, beginners to JavaScript or beginners to front end frameworks. If you are not a beginner, this tutorial might be a bit slow for you, but I invite you to read it and help me improve it. Whatever your level please let me know where you get stuck, what things are not clearly explained or lack sufficient detail. Most easily reached by tweet or leave a comment at the bottom.

Getting Started

First we need to install node.js. Node.js is javascript used for writing applications on a server. We won't be doing anything directly with node, but will use node package manager (npm) to install ember CLI (command line interface). If the last two sentences went completely over your head and you're like, 'WTF? I thought he said this was a beginners tutorial on ember, why's this jackass chatting about node and package managers, what's a package manager? How is he predicting my thoughts so accurately...' don't worry, it's not important for the tutorial, just get node installed from node.js. Once installed, if you open a terminal and type.

node -v

You should see the version (-v) of node.js e.g. v6.x.x.

Now lets use npm to install ember CLI which will give us all of the ember goodness.

npm install ember-cli -g
ember -v

The -g on npm install, installs the package globally, compared to in the folder we are currently in. This means we can use the ember-cli from anywhere on the computer. In this tutorial I've used version: 2.10.x of ember-cli, and also for ember & ember-data. Use this version or higher for this tutorial.

Navigate to a folder in the terminal where you want to keep the app. Using ember CLI to make a new app is as easy as.

ember new todo-mvc

If you've used rails before you'll be familiar with this type of generator. We've basically told ember-cli to set up a load of files and install the required other packages of code to make an ember app. There will be a lot of output from your terminal, the upshot of it being your new app is created in a todo-mvc folder so navigate to it.

cd todo-mvc

Take some time to look at the structure of your app. Most of the files we'll be using or creating will be under the app folder. At this point I would suggest opening the contents of that folder in a text editor, sublime text or atom (Mac only) are good options.

Lets start the app.

ember serve

The output from your terminal will be this.

Livereload server on http://localhost:49152
Serving on http://localhost:4200/
// Some build information about the time it took to build //

The important line is Serving on http://localhost:4200 this means that if you navigate to localhost:4200 in your browser you should see your ember app. You should see this page:

Great! A friendly message from the ember team. You could checkout the Quick Start, but why would you want to do that? You've got the Painfully Long & Indepth Start right here. The Ember Guides are a great resource to either get to grips with Ember or to further your understanding, although when I first started with Ember I found them a little daunting and sometimes incomprehensible. That was back in early 2015 and since then the guides have improved and so have I. I now find the guides & API documentation the first place I turn to if I'm stuck with anything in Ember.

Let's go ahead though and remove this welcome page, and in the process start to use ember to build our web app. To remove the welcome page, the instructions are there on the bottom of the page.

To remove this welcome message, remove the ember-welcome-page add-on from your package.json file

Open up package.json in your text editor and remove this line:

"ember-welcome-page": "^1.0.1",

Once we've removed this line, stop the server with Ctrl + C in the terminal where it's running. Now we tell our package manger to remove the packages we've removed.

npm prune

And restart the server with $ ember serve again.

You should now see a blank page. Congratulations!!

Seriously let's do something now. At the base of all Ember apps is the application route. In the next section I'll explain routes in more detail, but for now if we add an application template, it will get loaded when the application starts. Now we can add a template file by simply creating a new file in the right place, but ember provides something called a generator to automatically create files for us. When creating one file a generator isn't hugely necessary, but when you want to create multiple files in different locations, with some starter code in them, these generators are a life saver, especially when starting Ember and not entirely confident where new files should go. In the terminal type:

ember generate template application

Which will have the following output:

installing template
create app/templates/application.hbs

The generator has simply created one file for us in the app/templates folder. The hbs extension is a handlebars, which is a templating language. Ember further extends handlebars with something called HTMLbars which adds ember specific functionality. Templating languages allow you to perform some basic code in your html. For example output a variable, loop over an array or check if some variable is true or false. We will do all these things in this tutorial so don't worry if that doesn't make sense right now.

<h2>Welcome to the Jungle</h2>

Check the browser and your Ember app should have automatically reloaded with your new title.

The Router && Routes

Our first bit of proper Ember. I'm excited.

Together the router and routes determine the structure of our app and how it responds to users. When a user visits www.myapp.com/about, what do we want it to do? If they visit www.myapp.com/todos/1 what do we want to show at this URL? In these examples we'd want to load content about the app for the first url and fetch the todo of an ID of 1 from our database for the second url.

There are two concepts we need to get our head around in order to understand routing in Ember and we really should take the effort to understand this because it is, in my opinion, what sets apart ember from other frameworks, it's what makes it a framework for building ambitious web apps.

Route: A route will load the template and data required.

Router: The router determines which Route (or set of Routes) to load based on a given URL.

Let's look at this process with our todos route. First, download the Ember Inspector which we will need throughout the tutorial. It is a chrome or firefox addon. It will appear in developer tools as a tab (open with cmd+alt+i chrome mac). We'll be using it in a bit.

Open up our router, app/router.js. That's right, the router is so important its not even hidden in a folder. Lets add our 'todos' route inside the Router.map.

We now have a todos route the '/' URL (e.g. www.localhost:4200/). Why '/'? This is the path we specify in the route definition. We could put it at '/shitToDo' path or we could leave it blank and then it would be at the '/todos' path by default. We are telling our application, if someone visits this URL, load the todos route. We will create that route object shortly, which will tell the app what template and data to load. Finally the last line export default Router; is part of the latest release of Javascript that means you don't get global variables. Global variables are bad, if you don't know why, you can google it or just trust me when I say...

Open up developer tools on our application page in the browser and hit the Ember tab. Lets look at our routes.

s

I've checked 'Current Route only', this is to reduce the noise. We have an application route and underneath this is our todos route. Application route you get for free, it's at the top of all route trees in Ember. The application route its a good place to put anything you want to be on every route in your app, for example a header and footer. We've already seen the application template, app/templates/application.hbs. Lets edit it now.

We have some html with one variable, {{outlet}}. I will explain what this variable is in the next section of the tutorial, #keepReading.

Where is the application route? It's not in the routes folder, and this is because it's auto generated by Ember. We could create it if we wanted non-default behaviour. The default behaviour of a route is to render the template with the same name.

Our todos route has a model hook. The term hook is synonymous for function. This model function will be run when the route is loaded. Inside our model function we create an array [] of javascript objects {} that represent a todo. The array is called todos and this is returned by the function. This means that our todos route has now rendered the todos.hbs template (by default) and it's also loaded our todos data. The last part is to show that the template has access to the model data. Open up app/templates/todos.hbs.

The model array is being looped over with the handlebars {{#each}} helper. Each instance we are printing out the {{todo.title}}. The model hook from the route sets the variable to be called model by default for the template, again we could change it to be more explicit, like 'todos', but we won't right now.

Those last two sections were a lot of information. I would recommend re-reading them to cement your understanding of how the router and routes work in Ember.

Then a bonus bit of information. We created some files manually here, we added some boiler plate code like import Ember from 'ember'. We could have used a generator to do this instead.

Planning our Routes

The more I've done Ember, the more I've realised how important it is to plan your applications routes at the beginning. I don't know if it's realistic to do this when starting a new project, but in this case I know the exact end result so I'm going to do it.

We need three routes nested under our todos. index, incomplete and complete. The first route will list all the todos. The other two routes will filter the todos accordingly. They will have the following URLs.

We have nested two routes under our todos route. The index route is not listed, but in Ember a nested set of routes will automatically have an index route. Unless a different path argument is provided the URL will be the same as the name of the route, e.g. '/complete'.

The last thing we need to do is to move the list of todos from the todos template, app/templates/todos.hbs, down to the index template, app/templates/todos/index.hbs. Remember we use an {{outlet}} in the parent template to indicate where the child template should be loaded, and the todos route is the parent of index, complete and uncomplete routes, which means any content in there will be loaded on all three of these routes, so lets edit app/templates/todos.hbs.

The 'modelFor' function finds the model for another Route, in this case the for the parent 'todos' route.

Now go look at www.localhost:4200/ and open Ember Inspector and look at the view tree.

You will see that the index template is being rendered under the todos route.

We now have a solid foundation for our app.

Components

Components are discrete reusable blocks of code that produce things (components) that users can interact with. For example an input box is a component which you see in use all over the internet. It has standard behaviours, i.e. allowing the user to write in it. In any html we can generate this input box by using the <input> tag. the development community have come to realise that these common components are really useful and if we can allow any developer to write new ones, that could also be useful, for example a standard <calendar> tag would be useful on many websites.

A (standard for components)[http://w3c.github.io/webcomponents/spec/custom/] is currently being developed by the powers that be. Ember implements components and tries to stay close to the predicted standards so in the future you can easily switch in the standard components.

Let's think about the components in our todos application. It seems to me that we have two major components in our application. A todo-list and a todo-item.

Our component consists of two parts, a template, app/templates/components/todo-item.hbs, for displaying it and a javascript file, app/components/todo-item.js, for calculating any variables and handling user events.

A todo-item template contains the html mark up. Open up app/templates/components/todo-item.hbs.

In our todo-item we are outputting the {{todo.title}} variable so we need to make sure we pass in a todo when we use the component. On the todos.index template you can see we are doing this inside the {{#each}} block. We are also checking the status of todo.complete and if it is, checking the box and applying a class of completed with checked="{{if todo.complete 'checked'}}" and {{if todo.complete 'completed'}}.

There is one final thing we need to do in the javascript for our component, app/components/todo-item.js.

Every component will be inserted in a html tag by ember, by default this would be <div>, but in this case we wanted a <li> to surround our component so we specified that here with tagName property. We could have put the <li> in the template, which might have been clearer but leads to spurious extra divs (mostly harmless) in the code.

Now lets make the todo-list.

$ ember g component todo-list

*g is a shortcut for generate

Our todo-list template will contain most of the mark up from our todos template, lets edit app/templates/components/todo-list.hbs.

The {{outlet}}will be rendered in the {{yield}} location of our todo-list component. Remember the {{outlet}} is our nested route, todos.index. We are also passing in the todos model to our todo-list now, we will write code that uses the model later in this tutorial.

Modelling a Todo

A model is like a blueprint for your data. Each todo item in our list will be an instance of our Todo model. We can use our ember generator to generate a model for us.

Each instance of our Todo model will have a title and an isCompleted field. The title field will be a string and the isComplete field will be a boolean (true or false). We are using ember data, which is the library used to manage data for ember apps. It's maintained by the ember team and should work out of the box.

Data Mirage

Previously our model data was an array of javascript object. This was a quick and dirty fix for an example but when building an app, we will usually request data from a server. We will not build a server in this tutorial, what we will do is use a package called ember-cli-mirage that will mock a server for us. We can install it easily with ember-cli.

$ ember install ember-cli-mirage

Now in app/routes/todos.js we will use ember data to request data instead of using the array variable.

This code will create a todo with title "Todo title 1" and randomly selected complete state of true or false.

We call the factory in app/mirage/scenarios/default.js.

export default function(server) {
server.createList('todo', 3);
}

The scenario instructs our dummy server to create 3 todos.

Finally restart your server in the terminal.

And that's it. We are now mocking our data. If you didn't follow that, it's probably because I didn't go through it in great detail. Don't worry about it, learning Mirage not the main goal of this tutorial.

Create a New Todo

Lets add another todo to our list. In order we need to do this we need to create a new component, todo-input.

Firstly we inject the store into the component. The store is part of ember-data, it contains data and allows us to query for more data. In the submitTodo action we are checking that there is a title and if there is we tell the store to create a new record for us with the value from the input field. Then we clear the input with this.set('title', '');.

Complete a Todo

We want to be able to toggle the value of complete from true to false and back again through our app for a todo. This requires change to our checkbox input, we will use another handlebars {{input}} helper. Our component app/templates/components/todo-item.hbs will change to.

When the input tag is rendered it uses the current value of the todos complete property to determine whether the input is checked or not, checked=todo.complete. When the check value is changed, Ember will update the model value for us without having to write any code. Yep, the toggle action is automatically handled for us, how cool is that?!? (Very)

As a quick sanity check, we should now be able to do 2 things from our initial list of requirements for the todo-item component:

it should display the todo item

it should have a way to mark the todo as complete or incomplete

it should be editable (TODO)

it should allow us to delete the todo (TODO)

If only I had some kind of todo list to keep track of these requirement!

We toggle the editing class on the isEditing property. We've used a block helper, {{#if}}. This gives us a simple if else block. If isEditing is true then render <input class='edit'>, if isEditing is false then render the other part after the {{else}}. It's important to note that in handlebars you can only check whether a property is true or false* and there is no else if option. Inside the else block we have another {{action}} helper. The action is called editTodo and is triggered by a doubleClick on the element.

First notice that we create a property on our component called editing. This is a display state of the todo-item component, which means it's not important to the rest of the wider application, so we manage it on the component. Components should be responsible for managing their own state. The action, editTodo, toggles the editing property. Finally classNameBindings will apply a class 'editing' to the <li> enclosing tag of our component. This is a good opportunity to checkout the API docs for Ember.Component which will explain how classNameBindings property works.

Now double click now renders an input box.

The next step is to change in <input> tag into a handlebars helper that will change the title value of the model. Our component will now look like this in app/templates/components/todo-item.hbs.

In this action we get the todo item, we then check whether the title is empty or not. If the title is empty we delete the todo, and once the server has responded, indicated by then, we toggle the editing property, if the title isn't empty we toggle the editing property.

Lets just hook it up to the 'x' button on the right of the todo. We need add another {{action}} to the handlebar helper for our destroy button in app/templates/components/todo-item.hbs.

<button class="destroy" {{action "deleteTodo"}}></button>

We've added a deleteTodo action for a button. We need to make our javascript send this action to the todos route from our components actions, open app/components/todo-item.js.

Nested Routes

At the beginning of the tutorial we created nested routes in our router. Lets use them to filter out the complete and incomplete todos. Let's first do this in our complete route, app/routes/complete.js.

Now if you visit localhost:4200/complete you will see only the complete todos on our list. The model() hook first returns the todos model with todos.filter('todo', then it goes through each todo and returns the ones where todo.get('complete') === true. The renderTemplate() hook tells ember to render the template for todos.index (app/templates/todos/index.hbs) and apply the model for this route as the model.

Our incomplete route will be almost identical, but with return !todo.get('complete'); in the filter function, our app/routes/incomplete.js file looks like this.

The {{#link-to}} helper allows us to create links to our applications routes, as defined by the router, and we can apply a custom class when the route is active, i.e. when we are at localhost:4200/incomplete that link will have a class of selected applied to it.

The remaining property contains an Ember computed property. A computed property is a property calculated using another property or a number of properties. Ember computed properties will observe properties and if any of them change, it will update the computed property.

In our remaining property, we are observing [email protected]. This means that we are looking at every instance (@each) of the todos and the complete property of each instance. This means that if the complete property changes on any of the todos, the computed property will recalculate. If you changed the title property of a todo, this function would not recalculate. The actual computed property is the number (.get('length')) of models where complete is false (model.filterBy('complete', false).

In our inflection property we our observing our other computed property, remaining. The computed property will be 'item' if there is only one todo remaining and 'items' in all other cases.

In order to add a button to clear the the complete todos for part two we need to figure out if we have completed any todos, how many we've completed and write an action to delete the completed ones. First the changes in our app/components/todo-list.js.

First we have an action, clearCompleted, which gets all the completed todos using filterBy method, which we've seen previously. We then delete each todo.

We've added two computed properties, hasCompleted, which finds out if we have this.get('completed') > 0 i.e. at least 1 completed todo. This property will be used in our template to allow us to decide whether or not to show the delete completed todos button using a handlebars {{#if}} block. The second computed property, completed, counts the number of completed todos. We also need to add the action to the button.

Final interaction, home straight, if you've made it here, don't give up now, if you've not made it here...

For this final functionality of toggling todos between complete and incomplete lets start with the components HTML and then work out what our javascript needs to do. Open up app/templates/components/todo-list.hbs.

Here we use the didInsertElement() hook, which is called the first time a component is inserted. In this function we calculate if there are any todos, todos.get('length') > 0, and,&&, if they are all complete,todos.isEvery('complete', true)then we setallAreDoneto true. In all other casesallAreDone` will be false.

When the checkbox is clicked, the triggered action sets the allAreDone property and sets all the todos to that status (true or false).

WOAH that's it, we're done. HIGH 5.

Conclusion

All good things must come to an end, but you are no where near the end, you're at the beginning of building ambitious web apps with ember.js. I've covered some of the basics of ember.js, but there is much more ember goodness out there to be consumed.