#Todo app tutorial in Ember Octane for those who know React
Tags: ember, tutorial, javascript, codenewbie
Published: 2020-04-19 11:31:12

Like many Ember developers I came across it when I started working in my current job. 

Ember has been around since 2011 (React was released in 2013) and is used by some well known websites like Linkedin, Apple Music and Twitch. Unlike Vue, Gatsby or React Ember, never was the sexy new toy every developer spent the weekend learning. Which I think is a shame as the framework is scalable and I find that it offers itself to collaboration far better than some other frameworks. 

So to encourage others to explore Ember here is a tutorial of how to build a TODO app with CRUD functionality.

####Why Am I comparing Ember and React?
React is the lingua franca of front end development. And, I find that, it’s always easier to learn a new language when it's directly compared with something you already know, even if the comparisons are not perfect.

**Bellow is my take on Ember and its community but feel free to skip to the tutorial below that.**

##React vs Ember
React is not a fussy framework you throw everything into one component: the DOM, a few functions, an API request, and the kitchen sink. And everything works. No matter how messy you are, React will figure it out. 
Ember likes tidiness. Ember likes order. In Ember everything has its place. One cannot be messy in Ember. 
React is awesome for solo weekend projects, Ember is great if you are working with others. 

###Quirks anyone new to Ember will face:

####Lack of resources 
Google “How to do … in React?’ and you will get thousands of resources from official documentation, to Stack Overflow, to opinion pieces on Medium and educational ones on Dev.to. Learning React I can't recall many cases where a Google search would fail to answer my question. 
Ember has fewer resources. There is the official documentation (which is amazing), a few blogs, and the Ember discord channel. This is a great list of Ember [resources](https://github.com/abhilashlr/emberjs2019-posts).

####It’s hard to stay anonymous
As React is known and used by so many developers I sometimes question if it can even be called a community. Ember is used by fewer developers and almost all developers that actively use it can be found on the Ember discord forum. The main event of the year is Ember Conf, and that gathers most of Ember devs. Another thing to flag up is that most developers who use Ember on a day-to-day basis work for a few dozen companies. Because of the size and the fact that the community is so centralised its hard to stay anonymous within it. 

####Community made of professionals
I have yet to meet developers who took up Ember as a fun weekend thing to learn. So in most cases we all learn Ember because companies we work for use Ember. Ember is a community of professionals, which also makes it interesting because once you get past the initial intimidation, everyone within it is your current, past, or future colleague. 


##Tutorial 
As a reminder I am a newbie to Ember teaching others so this tutorial is very much a practical how to get stuff to work here and now rather than a birds eye view of how things work in Ember. For documentation, check out [https://emberjs.com/](https://emberjs.com/). 

###Getting started. 
**Open your terminal** 

* 1 - Run *npm install -g ember-cli*

The version I am using now is 

```
ember --version
ember-cli: 3.16.0
node: 12.16.0
os: linux x64
```

* 2 - *ember new todo-app*
* 3 - *cd todo-app*
* 4 - open the app in your editor
* 5 - back in the terminal run 
*ember serve* or *npm start*
This will start the app and you can view it in *http://localhost:4200/*

The first thing to flag up is the folder and file structure in Ember vs that of React. 
The most basic app in React will have 


```
index.html 
style.css
index.js
```

You can throw everything into index.js (functions, api calls etc) never touching the html and the css files and it will work. 

In ember every new app will have:

```
App
	Components
	Controllers
	Helpers
	Models
	Routes
Styles
Templates
	Application.hbs
App.js
Index.html
Router.js
```

To get ‘Hello World’ printed on the screen go to **application.hbs** delete 
```{{outlet}}``` 
and paste in 
```<h1>Hello World</h1>``` 
now for our app change it back to 
```{{outlet}}```

Everything that would find its way into one component in React will be scattered between **Route**, **Component** and **Template** in Ember. 


**Template** is your html. Hbs stands for handlebar. The main thing to know is that Handlebars are logic less so no mapping or filtering within your html.

**Route**... the best way of thinking about it is that Route is your Component Will Mount (it's not really but for practical purposes of getting s*it done think of it that way). The data that you want to see on the screen when the page loads is fetched/axioed/requested in the Route.

**Component** is where you put any functions that will react to any user input, button clicks, essentially any user interactions. 

**Helpers** is where little reusable helper functions go. For example if you are converting Fahrenheit into Celsius this is where the function that does that goes. 

As for **Controllers** well to quote a question posed n Ember Guide ‘Should we use controllers in my application? I've heard they're going away!’

###Step 1 - Creating a route

In terminal type
```ember g route todo```

The output in your terminal will be:

```
installing route
  create app/routes/todo.js
  create app/templates/todo.hbs
updating router
  add route todo
installing route-test
  create tests/unit/routes/todo-test.js
```

###Step 2 - Displaying the todo list in console

Let's start by adding adding some existing to do's. 
Go to app/routes/todo.js the template should look something like:

```
import Route from '@ember/routing/route';

export default class TodoRoute extends Route {
}
```

To add the todo dataset add the model to the route:

```
import Route from "@ember/routing/route";

export default class TodoRoute extends Route {
  model() {
    return [
      {
        id: 1,
        todo: "todo 1"
      },
      {
        id: 2,
        todo: "todo 2"
      },
      {
        id: 3,
        todo: "todo 3"
      }
    ];
  }
}
```

Now go to **app/templates/todo.hbs** delete whatever is in it and add: 

```
<h1>TODO app</h1>
{{log this.model}}
```

In your terminal run *ember serve* 

Open your web browser go to **http://localhost:4200/todo** you should see 'TODO app' on the page. Open the Inspector -> Console. In the console you should see your model array.

###Step 3 - Displaying the todo list on the screen

So here I make a decision that I will build the whole app in one component. Feel free to refactor it to be in separate components. I would argue that the 'list of todos' and the ''add new todo button' should be two separate components but I will let you figure out how to refactor this.

####Part 3.1 

**In Terminal run:**
*ember g component todo-app*

You will see the following in your terminal 

```
installing component
  create app/components/todo-app.hbs
  skip app/components/todo-app.js
  tip to add a class, run `ember generate component-class todo-app`
installing component-test
```

Go ahead and follow the 'tip' and run the command *ember generate component-class todo-app*.

Now if you go to **app/components** you will find **todo-app.hbs** and **todo-app.js**. 

Todo-app.hbs is your html, and todo-app.js is your logic and action handling part.

Let's go to todo-app.hbs delete whatever is there and add 
```<p>sanity check</p>```

If you now go to http://localhost:4200/todo or http://localhost:4200/ you will not see sanity check on the screen. 

To get **todo-app.hbs** displaying on the screen go to **todo.hbs** and add **<TodoApp />** to the file so that you have 

```
<h1>TODO app</h1>
<TodoApp />
{{log this.model}}
```

Now go to http://localhost:4200/todo - viola! Sanity check is displaying.
  
####Part 3.2

So if you go to **todo.hbs** and take out 
```{{log this.model}}``` 
and go to  **todo-app.hbs** and add it there

```
<p>sanity check</p>
{{log this.model}}
```

You will now get **undefined** in your console rather than the model. 

So let's pass the model from todo to the component todo-app by changing 
```<TodoApp />``` 
to 
```<TodoApp @model={{this.model}}/>``` 

Despite this change you will still get **undefined** because **this.model** was passed as **@model** to the component. 

So lets change 
```{{log this.model}}``` 
to 
```{{log @model}}``` 

Viola! You are back to square 0 with the model showing in console. Now let's actually display it. 

####Part 3.3

In React the most basic solution to displaying 3 todos would just be:

```
<ul>
	<li>{this.state.model[0].todo}</li>
	<li>{this.state.model[1].todo}</li>
	<li>{this.state.model[2].todo}</li>
</ul>
```

You can try writing something along these lines in **todo-app.hbs** but this will not work. So another option of displaying this in React is by using .map.

So something like this:

``` 
<ul>
  {this.state.model.map(todo => {
    return <li key={todo.id}>{todo.todo}</li>;
  })}
</ul>
```

Templates are logic less and this means that the javascript .map will not work however templates have helpers that can bring some logic to the template. 

We will be doing something similar to .map by using a [helper 'each'](https://guides.emberjs.com/v2.15.0/templates/displaying-a-list-of-items/).

So let's go to **todo-app.hbs** and add:

```
<ul>
  {{#each @model as |item|}}
  <li>{{item.todo}}</li>
  {{/each}}
</ul>
```

Nice! The todo list is displaying.


###Step 4 - Adding actions

Now lets add a textfield and a button so that you can add new todos to the list. 

On the html side the tags will be identical to the ones you would have used in a React component. So lets add this to **todo-app.hbs**:

```
<ul>
  {{#each @model as |item|}}
  <li>{{item.todo}}</li>
  {{/each}}
</ul>
<form>
  <input placeholder='Add todo' type='text' />
  <button type='submit'>Add</button>
</form>
```

This will display an input field and a button but of course this will not do anything, so it's finally time to look at **todo-app.js**. But before we do this let's see how this would have looked in React. 


####React view

```
import ReactDOM from "react-dom";
import React, { Component } from "react";

class Todo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      model: [
        {
          id: 1,
          todo: "todo 1"
        },
        {
          id: 2,
          todo: "todo 2"
        },
        {
          id: 3,
          todo: "todo 3"
        }
      ],
      text: ""
    };
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();
    const i = this.state.model[this.state.model.length - 1].id + 1;
    let newTodo = {
      todo: this.state.text,
      id: i
    };
    this.setState(prevState => ({
      model: prevState.model.concat(newTodo),
      text: ""
    }));
  }

  handleChange(e) {
    this.setState({
      text: e.target.value
    });
  }

  render() {
    return (
      <div>
        <h1>TODO LIST</h1>
        <ul>
          {this.state.model.map(todo => {
            return <li key={todo.id}>{todo.todo}</li>;
          })}
        </ul>
        <form onSubmit={this.handleSubmit}>
          <input value={this.state.text} onChange={e => this.handleChange(e)} />
          <button>Add</button>
        </form>
      </div>
    );
  }
}

ReactDOM.render(<Todo />, document.getElementById("root"));
```


####Back to Ember

Now lets write handleChange and handleSubmit in Ember. 

The React

```
  handleChange(e) {
    this.setState({
      text: e.target.value
    });
  }
```

barely changes as it becomes:

```
  @tracked
  text = "";
  
  @action
  onChange(e) {
    this.text = e.target.value;
  }
```

@tracked is your state but it's best you read about @tracker and @action in the [Ember guide](https://guides.emberjs.com/release/components/component-state-and-actions/). 

And handleSubmit goes from:

```
  handleSubmit(e) {
    e.preventDefault();
    const i = this.state.model[this.state.model.length - 1].id + 1;
    let newTodo = {
      todo: this.state.text,
      id: i
    };
    this.setState(prevState => ({
      model: prevState.model.concat(newTodo),
      text: ""
    }));
  }
```

to:

```
  @action
  submit(model, e) {
    e.preventDefault();
    const i = model[model.length - 1].id + 1;
    const newTodo = {
      id: i,
      todo: this.text
    };
    model.pushObject(newTodo);
  }
```
 

So **todo-app.js** ends up looking like this: 


```
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";

export default class TodoAppComponent extends Component {
  @tracked
  text = "";

  @action
  submit(model, event) {
    event.preventDefault();
    const i = model[model.length - 1].id + 1;
    const newTodo = {
      id: i,
      todo: this.text
    };
    model.pushObject(newTodo);
    console.log("add", model);
  }

  @action
  onChange(e) {
    this.text = e.target.value;
  }
}
```

###Step 5 connecting .js with .hbs

React render looks like this:

```
        <form onSubmit={this.handleSubmit}>
          <input value={this.state.text} onChange={e => 
            this.handleChange(e)} />
          <button>Add</button>
        </form>
```

in Ember your **todo-app.hbs** should look like this:

```
<form onsubmit={{fn this.submit @model}}>
  <input placeholder='Add todo' type='text' value={{this.text.todo}} 
    onchange={{fn this.onChange}} />
  <button type='submit'>Add</button>
</form>
```

So the full todo-app.hbs looks like this:

```
<ul>
  {{#each @model as |item|}}
  <li>{{item.todo}}</li>
  {{/each}}
</ul>
<form onsubmit={{fn this.submit @model}}>
  <input placeholder='Add todo' type='text' value={{this.text.todo}} onchange={{fn this.onChange}} />
  <button type='submit'>Add</button>
</form>
```

###Step 6 adding the Delete function

I have to admit delete will not look pretty in React or Ember because we are dealing with an array of objects. If it was an array of strings life would have been easier. 

In React it looks like this:

```
  removeItem(id) {
    let model = this.state.model;
    const index = model
    .map((file, index) => {
      if (file.id === id) {
        return index;
      } else return undefined
    })
    .filter(id => id !== undefined);
    model.splice(index, 1);
    this.setState([...model]);
  }
```

To a degree Ember follows the same general idea. You figure out the index, then you splice the array. 

There is a but. The HTML side will not re-render if you delete a 'todo’ from the array (I'm new to Ember, so send better solutions). So the work around that I have is: replace the item about to be deleted with an empty object and on the template add 'if empty don’t show’.

**todo-app.js**

```
  @action
  delete(model, item) {
    const index = model
      .map((file, index) => {
        if (file.id === item.id) {
          set(item, "id", null);
          set(item, "todo", null);
          return index;
        }
      })
      .filter(id => id !== undefined);
    model.splice(index[0], 1);
  }

```
and **todo-hbs.js**

```
<ul>
  {{#each @model as |item|}}
  {{#if item.id}}
  <li>{{item.todo}}</li>
  <button onclick={{fn this.delete @model item}} type='button'>delete</button>
  {{/if}}
  {{/each}}
</ul>
```

Again I am new to Ember so any prettier ways of achieving this without using Ember data are welcome. 

And with that Viola! The Todo App is done. It is missing editing individual Todo's but I will let you figure that out if you can't here is a the github [repo](https://github.com/JennyJudova/todo-app). 

And the React to do using a constructor is [here](https://codesandbox.io/s/todo-app-react-84286) the hooks version is [here](https://codesandbox.io/s/todo-app-react-hooks-305zu).