TypeScript support in Svelte - Learn web development

TypeScript support in Svelte
============================

* Previous
* Overview: Client-side JavaScript frameworks
* Next

In the last article we learned about Svelte stores and even implemented our own custom store to persist the app's information to Web Storage. We also had a look at using the transition directive to implement animations on DOM elements in Svelte.

We will now learn how to use TypeScript in Svelte applications. First we'll learn what TypeScript is and what benefits it can bring us. Then we'll see how to configure our project to work with TypeScript files. Finally we will go over our app and see what modifications we have to make to fully take advantage of TypeScript features.

|  |  |
| --- | --- |
| Prerequisites: |
 At minimum, it is recommended that you are familiar with the core
 HTML,
 CSS, and
 JavaScript languages, and
 have knowledge of the
 terminal/command line.

 You'll need a terminal with node and npm installed to compile and build
 your app.
  |
| Objective: |
 Learn how to configure and use TypeScript when developing Svelte
 applications.
  |

Note that our application is fully functional, and porting it to TypeScript is completely optional. There are different opinions about it, and in this chapter we will talk briefly about the pros and cons of using TypeScript. Even if you are not planning to adopt it, this article will be useful for allowing you to learn what it has to offer and help you make your own decision. If you are not interested at all in TypeScript, you can skip to the next chapter, where we will look at different options for deploying our Svelte applications, further resources, and more.

Code along with us
------------------

### Git

Clone the GitHub repo (if you haven't already done it) with:

```bash
git clone https://github.com/opensas/mdn-svelte-tutorial.git
```

Then to get to the current app state, run

```bash
cd mdn-svelte-tutorial/07-typescript-support
```

Or directly download the folder's content:

```bash
npx degit opensas/mdn-svelte-tutorial/07-typescript-support
```

Remember to run `npm install && npm run dev` to start your app in development mode.

### REPL

Unfortunately, TypeScript support is not yet available in the REPL.

TypeScript: optional static typing for JavaScript
-------------------------------------------------

TypeScript is a superset of JavaScript that provides features such as optional static typing, classes, interfaces, and generics. The goal of TypeScript is to help catch mistakes early through its type system and make JavaScript development more efficient. One of the big benefits is enabling IDEs to provide a richer environment for spotting common errors as you type the code.

Best of all, JavaScript code is valid TypeScript code; TypeScript is a superset of JavaScript. You can rename most of your `.js` files to `.ts` files and they will just work.

Our TypeScript code will be able to run everywhere JavaScript can run. How is that possible? TypeScript "transpiles" our code to vanilla JavaScript. That means that it parses TypeScript code and produces the equivalent vanilla JavaScript code for browsers to run.

**Note:** If you are curious about how TypeScript transpiles our code to JavaScript, you can have a look at the TypeScript Playground.

First-class TypeScript support has been Svelte's most requested feature for quite some time. Thanks to the hard work of the Svelte team, together with many contributors, we have an official solution ready to be put to the test. In this section we'll show you how to set up a Svelte project with TypeScript support to give it a try.

Why TypeScript?
---------------

TypeScript's main advantages are:

* Early spotted bugs: The compiler checks types at compile time and provides error reporting.
* Readability: Static typing gives the code more structure, making it self-documenting and more readable.
* Rich IDE support: Type information allows code editors and IDEs to offer features like code navigation, autocompletion, and smarter hints.
* Safer refactoring: Types allows IDEs to know more about your code, and assist you while refactoring large portions of your code base.
* Type inference: Enables you to take advantage of many TypeScript features even without declaring variable types.
* Availability of new and future JavaScript features: TypeScript transpiles many recent JavaScript features to plain old-school JavaScript, allowing you to use them even on user-agents that don't support them natively yet.

TypeScript also has some disadvantages:

* Not true static typing: Types are only checked at compile time, and they are removed from the generated code.
* Steep learning curve: Even though TypeScript is a superset of JavaScript and not a completely new language, there is a considerable learning curve, especially if you have no experience at all with static languages like Java or C#.
* More code: You have to write and maintain more code.
* No replacement for automatic tests: Even though types might help you catch several bugs, TypeScript is not a true replacement for a comprehensive suite of automated tests.
* Boilerplate code: Working with types, classes, interfaces, and generics can lead to over-engineered code bases.

There seems to be a broad consensus that TypeScript is particularly well suited for large-scale projects, with many developers working on the same codebase. And it is indeed being used by several large-scale projects, like Angular 2, Vue 3, Ionic, Visual Studio Code, Jest, and even the Svelte compiler. Nevertheless, some developers prefer to use it even on small projects like the one we are developing.

In the end, it's your decision. In the following sections we hope to give you more evidence to make up your mind about it.

Creating a Svelte TypeScript project from scratch
-------------------------------------------------

You can start a new Svelte TypeScript project using the standard template. All you have to do is run the following terminal commands (run them somewhere where you are storing your Svelte test projects — it creates a new directory):

```bash
npx degit sveltejs/template svelte-typescript-app

cd svelte-typescript-app

node scripts/setupTypeScript.js
```

This creates a starter project that includes TypeScript support, which you can then modify as you wish.

Then you'll have to tell npm to download dependencies and start the project in development mode, as we usually do:

```bash
npm install

npm run dev
```

Adding TypeScript support to an existing Svelte Project
-------------------------------------------------------

To add TypeScript support to an existing Svelte project, you can follow these instructions. Alternatively, you can download the `setupTypeScript.js` file to a `scripts` folder inside your project's root folder, and then run `node scripts/setupTypeScript.js`.

You can even use `degit` to download the script. That's what we will do to start porting our application to TypeScript.

**Note:** Remember that you can run `npx degit opensas/mdn-svelte-tutorial/07-typescript-support svelte-todo-typescript` to get the complete to-do list application in JavaScript before you start porting it to TypeScript.

Go to the root directory of the project and enter these commands:

```bash
npx degit sveltejs/template/scripts scripts       # download script file to a scripts folder

node scripts/setupTypeScript.js                   # run it
Converted to TypeScript.
```

You will need to re-run your dependency manager to get started.

```bash
npm install                                       # download new dependencies

npm run dev                                       # start the app in development mode
```

These instructions apply to any Svelte project you'd like to convert to TypeScript. Just take into account that the Svelte community is constantly improving Svelte TypeScript support, so you should run `npm update` regularly to take advantage of the latest changes.

**Note:** if you find any trouble working with TypeScript inside a Svelte application, have a look at this troubleshooting/FAQ section about TypeScript support.

As we said before, TypeScript is a superset of JavaScript, so your application will run without modifications. Currently you will be running a regular JavaScript application with TypeScript support enabled, without taking advantage of any of the features that TypeScript provides. You can now start adding types progressively.

Once you have TypeScript configured, you can start using it from a Svelte component by just adding a `<script lang='ts'>` at the beginning of the script section. To use it from regular JavaScript files, just change the file extension from `.js` to `.ts`. You'll also have to update any corresponding import statements (don't include the `.ts` in your `import` statements; TypeScript chose to omit the extensions).

**Note:** Using TypeScript in component markup sections is not supported yet. You'll have to use JavaScript from the markup, and TypeScript in the `<script lang='ts'>` section.

Improved developer experience with TypeScript
---------------------------------------------

TypeScript provides code editors and IDEs with lots of information to allow them to deliver a friendlier development experience.

We'll use Visual Studio Code to do a quick test and see how we can get autocompletion hints and type-checking as we're writing components.

**Note:** If you don't wish to use VS Code, we also provide instructions for using TypeScript error checking from the terminal instead, slightly later on.

There is work in progress to support TypeScript in Svelte projects in several code editors; the most complete support so far is available in the Svelte for VS Code extension, which is developed and maintained by the Svelte team. This extension offers type checking, inspecting, refactoring, intellisense, hover-information, auto-completion, and other features. This kind of developer assistance is another good reason to start using TypeScript in your projects.

**Note:** Make sure you are using Svelte for VS Code and NOT the old "Svelte" by James Birtles, which has been discontinued. In case you have it installed, you should uninstall it and install the official Svelte extension instead.

Assuming you are inside the VS Code application, from the root of your project's folder, type `code .` (the trailing dot tells VS Code to open the current folder) to open the code editor. VS Code will tell you that there are recommended extensions to install.

![Dialog box saying this workspace has extension recommendations, with options to install or show a list](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/01-vscode-extension-recommendations.png)

Clicking *Install all* will install Svelte for VS Code.

![Svelte for VS Code extension information](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/02-svelte-for-vscode.png)

We can also see that the `setupTypeScript.js` file made a couple of changes to our project. The `main.js` file has been renamed to `main.ts`, which means that VS Code can provide hover-information on our Svelte components:

![VS Code screenshot showing that when you add type="ts" to a component, it gives you three dot alert hints](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/03-vscode-hints-in-main-ts.png)

We also get type checking for free. If we pass an unknown property in the options parameter of the `App` constructor (for example a typo like `traget` instead of `target`), TypeScript will complain:

![Type checking in VS Code - App object has been given an unknown property target](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/04-vscode-type-checking-in-main-ts.png)

In the `App.svelte` component, the `setupTypeScript.js` script has added the `lang="ts"` attribute to the `<script>` tag. Moreover, thanks to type inference, in many cases we won't even need to specify types to get code assistance. For example, if you start adding an `ms` property to the `Alert` component call, TypeScript will infer from the default value that the `ms` property should be a number:

![VS Code type inference and code hinting - ms variable should be a number](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/05-vscode-type-inference-and-code-assistance.png)

And if you pass something that is not a number, it will complain about it:

![Type checking in VS Code - App object has been given an unknown property target](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/06-vscode-type-checking-in-components.png)

The application template has a `check` script configured that runs `svelte-check` against your code. This package allows you to detect errors and warnings normally displayed by a code editor from the command line, which makes it pretty useful for running it in a continuous integration (CI) pipeline. Just run `npm run check` to check for unused CSS, and return A11y hints and TypeScript compile errors.

In this case, if you run `npm run check` (either in the VS Code console or terminal) you will get the following error:

![Check command being run inside VS Code showing type error, ms variable should be assigned a number](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/07-vscode-svelte-check.png)

Even better, if you run it from the VS Code integrated terminal (you can open it with the `Ctrl` + ``` keyboard shortcut), `Cmd`/`Ctrl` clicking on the file name will take you to the line containing the error.

You can also run the `check` script in watch mode with `npm run check -- --watch`. In this case, the script will execute whenever you change any file. If you are running this in your regular terminal, keep it running in the background in a separate terminal window so that it can keep reporting errors but won't interfere with other terminal usage.

Creating a custom type
----------------------

TypeScript supports structural typing. Structural typing is a way of relating types based solely on their members, even if you do not explicitly define the type.

We'll define a `TodoType` type to see how TypeScript enforces that anything passed to a component expecting a `TodoType` will be structurally compatible with it.

1. Inside the `src` folder create a `types` folder.
2. Add a `todo.type.ts` file inside it.
3. Give `todo.type.ts` the following content:

```ts
export type TodoType = {
  id: number;
  name: string;
  completed: boolean;
};
```

**Note:** The Svelte template uses svelte-preprocess 4.0.0 to support TypeScript. From that version onward you have to use `export`/`import` type syntax to import types and interfaces. Check this section of the troubleshooting guide for more information.
4. Now we'll use `TodoType` from our `Todo.svelte` component. First add the `lang="ts"` to our `<script>` tag.
5. Let's `import` the type and use it to declare the `todo` property. Replace the `export let todo` line with the following:

```ts
import type { TodoType } from "../types/todo.type";

export let todo: TodoType;
```

**Note:** Another reminder: When importing a `.ts` file, you have to omit the extension. Check the `import` section of the TypeScript manual for more information.
6. Now from `Todos.svelte` we will instantiate a `Todo` component with a literal object as its parameter before the call to the `MoreActions` component, like this:

```svelte
<hr />

<Todo todo={ { name: 'a new task with no id!', completed: false } } />

<!-- MoreActions -->
<MoreActions {todos}
```

7. Add the `lang='ts'` to the `<script>` tag of the `Todos.svelte` component so that it knows to use the type checking we have specified.
 We will get the following error:

 ![Type error in VS Code, Todo Type object requires an id property.](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/08-vscode-structural-typing.png)

By now you should get an idea about the kind of assistance we can get from TypeScript when building Svelte projects.

Now we will undo these changes in order to start porting our application to TypeScript, so we won't be bothered with all the check warnings.

1. Remove the flawed to-do and the `lang='ts'` attribute from the `Todos.svelte` file.
2. Also remove the import of `TodoType` and the `lang='ts'` from `Todo.svelte`.

We'll take care of them properly later on.

Porting our to-do list app to TypeScript
----------------------------------------

Now we are ready to start porting our to-do list application to take advantage of all the features that TypeScript offers to us.

Let's start by running the check script in watch mode inside the project root:

```bash
npm run check -- --watch
```

This should output something like the following:

```bash
svelte-check "--watch"

Loading svelte-check in workspace: ./svelte-todo-typescript
Getting Svelte diagnostics...
====================================
svelte-check found no errors and no warnings
```

Note that if you are using a supporting code editor like VS Code, a simple way to start porting a Svelte component is to just add the `<script lang='ts'>` at the top of your component and look for the three-dotted hints:

![VS Code screenshot showing that when you add type="ts" to a component, it gives you three dot alert hints](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/09-vscode-alert-hints.png)

### Alert.svelte

Let's start with our `Alert.svelte` component.

1. Add `lang="ts"` into your `Alert.svelte` component's `<script>` tag. You'll see some warnings in the output of the `check` script:

```bash
npm run check -- --watch
```

```
> svelte-check "--watch"

./svelte-todo-typescript
Getting Svelte diagnostics...
====================================

./svelte-todo-typescript/src/components/Alert.svelte:8:7
Warn: Variable 'visible' implicitly has an 'any' type, but a better type may be inferred from usage. (ts)
  let visible

./svelte-todo-typescript/src/components/Alert.svelte:9:7
Warn: Variable 'timeout' implicitly has an 'any' type, but a better type may be inferred from usage. (ts)
  let timeout

./svelte-todo-typescript/src/components/Alert.svelte:11:28
Warn: Parameter 'message' implicitly has an 'any' type, but a better type may be inferred from usage. (ts)
Change = (message, ms) => {

./svelte-todo-typescript/src/components/Alert.svelte:11:37
Warn: Parameter 'ms' implicitly has an 'any' type, but a better type may be inferred from usage. (ts)
(message, ms) => {
```

2. You can fix these by specifying the corresponding types, like so:

```ts
export let ms = 3000

  let visible: boolean
  let timeout: number

  const onMessageChange = (message: string, ms: number) => {
    clearTimeout(timeout)
    if (!message) {               // hide Alert if message is empty
```

**Note:** There's no need to specify the `ms` type with `export let ms:number = 3000`, because TypeScript is already inferring it from its default value.
### MoreActions.svelte

Now we'll do the same for the `MoreActions.svelte` component.

1. Add the `lang='ts'` attribute, like before. TypeScript will warn us about the `todos` prop and the `t` variable in the call to `todos.filter((t) =>...)`.
```

Warn: Variable 'todos' implicitly has an 'any' type, but a better type may be inferred from usage. (ts)
  export let todos

Warn: Parameter 't' implicitly has an 'any' type, but a better type may be inferred from usage. (ts)
  $: completedTodos = todos.filter((t) => t.completed).length
```

2. We will use the `TodoType` we already defined to tell TypeScript that `todos` is a `TodoType` array. Replace the `export let todos` line with the following:

```ts
import type { TodoType } from "../types/todo.type";

export let todos: TodoType[];
```

Notice that now TypeScript can infer that the `t` variable in `todos.filter((t) => t.completed)` is of type `TodoType`. Nevertheless, if we think it makes our code easier to read, we could specify it like this:

```ts
$: completedTodos = todos.filter((t: TodoType) => t.completed).length;
```

Most of the time, TypeScript will be able to correctly infer the reactive variable type, but sometimes you might get an "implicitly has an 'any' type" error when working with reactive assignments. In those cases you can declare the typed variable in a different statement, like this:

```ts
let completedTodos: number;
$: completedTodos = todos.filter((t: TodoType) => t.completed).length;
```

You can't specify the type in the reactive assignment itself. The statement `$: completedTodos: number = todos.filter[...]` is invalid. For more information, read How do I type reactive assignments? / I get an "implicitly has type 'any' error".

### FilterButton.svelte

Now we'll take care of the `FilterButton` component.

1. Add the `lang='ts'` attribute to the `<script>` tag, as usual. You'll notice there are no warnings — TypeScript infers the type of the filter variable from the default value. But we know that there are only three valid values for the filter: all, active, and completed. So we can let TypeScript know about them by creating an enum Filter.
2. Create a `filter.enum.ts` file in the `types` folder.
3. Give it the following contents:

```ts
export enum Filter {
  ALL = "all",
  ACTIVE = "active",
  COMPLETED = "completed",
}
```

4. Now we will use this from the `FilterButton` component. Replace the content of the `FilterButton.svelte` file with the following:

```svelte
<!-- components/FilterButton.svelte -->
<script lang="ts">
 import { Filter } from "../types/filter.enum";

 export let filter: Filter = Filter.ALL;
</script>

<div class="filters btn-group stack-exception">
  <button class="btn toggle-btn" class:btn\_\_primary={filter === Filter.ALL} aria-pressed={filter === Filter.ALL} on:click={()=> filter = Filter.ALL} >
    <span class="visually-hidden">Show</span>
    <span>All</span>
    <span class="visually-hidden">tasks</span>
  </button>
  <button class="btn toggle-btn" class:btn\_\_primary={filter === Filter.ACTIVE} aria-pressed={filter === Filter.ACTIVE} on:click={()=> filter = Filter.ACTIVE} >
    <span class="visually-hidden">Show</span>
    <span>Active</span>
    <span class="visually-hidden">tasks</span>
  </button>
  <button class="btn toggle-btn" class:btn\_\_primary={filter === Filter.COMPLETED} aria-pressed={filter === Filter.COMPLETED} on:click={()=> filter = Filter.COMPLETED} >
    <span class="visually-hidden">Show</span>
    <span>Completed</span>
    <span class="visually-hidden">tasks</span>
  </button>
</div>
```

Here we are just importing the `Filter` enum and using it instead of the string values we used previously.

### Todos.svelte

We will also use the `Filter` enum in the `Todos.svelte` component.

1. First, add the `lang='ts'` attribute to it, as before.
2. Next, import the `Filter` enum. Add the following `import` statement below your existing ones:

```js
import { Filter } from "../types/filter.enum";
```

3. Now we will use it whenever we reference the current filter. Replace your two filter-related blocks with the following:

```ts
let filter: Filter = Filter.ALL;
const filterTodos = (filter: Filter, todos) =>
  filter === Filter.ACTIVE
    ? todos.filter((t) => !t.completed)
    : filter === Filter.COMPLETED
      ? todos.filter((t) => t.completed)
      : todos;

$: {
  if (filter === Filter.ALL) {
    $alert = "Browsing all todos";
  } else if (filter === Filter.ACTIVE) {
    $alert = "Browsing active todos";
  } else if (filter === Filter.COMPLETED) {
    $alert = "Browsing completed todos";
  }
}
```

4. `check` will still give us some warnings from `Todos.svelte`. Let's fix them.
 Start by importing the `TodoType` and telling TypeScript that our `todos` variable is an array of `TodoType`. Replace `export let todos = []` with the following two lines:

```ts
import type { TodoType } from "../types/todo.type";

export let todos: TodoType[] = [];
```

5. Next we'll specify all the missing types. The variable `todosStatus`, which we used to programmatically access the methods exposed by the `TodosStatus` component, is of type `TodosStatus`. And each `todo` will be of type `TodoType`.
 Update your `<script>` section to look like this:

```ts
import FilterButton from "./FilterButton.svelte";
import Todo from "./Todo.svelte";
import MoreActions from "./MoreActions.svelte";
import NewTodo from "./NewTodo.svelte";
import TodosStatus from "./TodosStatus.svelte";
import { alert } from "../stores";

import { Filter } from "../types/filter.enum";

import type { TodoType } from "../types/todo.type";

export let todos: TodoType[] = [];

let todosStatus: TodosStatus; // reference to TodosStatus instance

$: newTodoId =
  todos.length > 0 ? Math.max(...todos.map((t) => t.id)) + 1 : 1;

function addTodo(name: string) {
  todos = [...todos, { id: newTodoId, name, completed: false }];
  $alert = `Todo '${name}' has been added`;
}

function removeTodo(todo: TodoType) {
  todos = todos.filter((t) => t.id !== todo.id);
  todosStatus.focus(); // give focus to status heading
  $alert = `Todo '${todo.name}' has been deleted`;
}

function updateTodo(todo: TodoType) {
  const i = todos.findIndex((t) => t.id === todo.id);
  if (todos[i].name !== todo.name)
    $alert = `todo '${todos[i].name}' has been renamed to '${todo.name}'`;
  if (todos[i].completed !== todo.completed)
    $alert = `todo '${todos[i].name}' marked as ${
 todo.completed ? "completed" : "active"
 }`;
  todos[i] = { ...todos[i], ...todo };
}

let filter: Filter = Filter.ALL;
const filterTodos = (filter: Filter, todos: TodoType[]) =>
  filter === Filter.ACTIVE
    ? todos.filter((t) => !t.completed)
    : filter === Filter.COMPLETED
      ? todos.filter((t) => t.completed)
      : todos;

$: {
  if (filter === Filter.ALL) {
    $alert = "Browsing all todos";
  } else if (filter === Filter.ACTIVE) {
    $alert = "Browsing active todos";
  } else if (filter === Filter.COMPLETED) {
    $alert = "Browsing completed todos";
  }
}

const checkAllTodos = (completed: boolean) => {
  todos = todos.map((t) => ({ ...t, completed }));
  $alert = `${completed ? "Checked" : "Unchecked"} ${todos.length} todos`;
};
const removeCompletedTodos = () => {
  $alert = `Removed ${todos.filter((t) => t.completed).length} todos`;
  todos = todos.filter((t) => !t.completed);
};
```

### TodosStatus.svelte

We are encountering the following errors related to passing `todos` to the `TodosStatus.svelte` (and `Todo.svelte`) components:
```

./src/components/Todos.svelte:70:39
Error: Type 'TodoType[]' is not assignable to type 'undefined'. (ts)
  <TodosStatus bind:this={todosStatus} {todos} />

./src/components/Todos.svelte:76:12
Error: Type 'TodoType' is not assignable to type 'undefined'. (ts)
     <Todo {todo}
```

This is because the `todos` prop in the `TodosStatus` component has no default value, so TypeScript has inferred it to be of type `undefined`, which is not compatible with an array of `TodoType`. The same thing is happening with our Todo component.

Let's fix it.

1. Open the file `TodosStatus.svelte` and add the `lang='ts'` attribute.
2. Then import the `TodoType` and declare the `todos` prop as an array of `TodoType`. Replace the first line of the `<script>` section with the following:

```ts
import type { TodoType } from "../types/todo.type";

export let todos: TodoType[];
```

3. We will also specify the `headingEl`, which we used to bind to the heading tag, as an `HTMLElement`. Update the `let headingEl` line with the following:

```ts
let headingEl: HTMLElement;
```

4. Finally, you'll notice the following error reported, related to where we set the `tabindex` attribute. That's because TypeScript is type checking the `<h2>` element and expects `tabindex` to be of type `number`.

 ![Tabindex hint inside VS Code, tabindex expects a type of number, not string](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/10-vscode-tabindex-hint.png)
 To fix it, replace `tabindex="-1"` with `tabindex={-1}`, like this:

```svelte
<h2 id="list-heading" bind:this="{headingEl}" tabindex="{-1}">
  {completedTodos} out of {totalTodos} items completed
</h2>
```

This way TypeScript can prevent us from incorrectly assigning it to a string variable.
### NewTodo.svelte

Next we will take care of `NewTodo.svelte`.

1. As usual, add the `lang='ts'` attribute.
2. The warning will indicate that we have to specify a type for the `nameEl` variable. Set its type to `HTMLElement` like this:

```ts
let nameEl: HTMLElement; // reference to the name input DOM node
```

3. Last for this file, we need to specify the correct type for our `autofocus` variable. Update its definition like this:

```ts
export let autofocus: boolean = false;
```

### Todo.svelte

Now the only warnings that `npm run check` emits are triggered by calling the `Todo.svelte` component. Let's fix them.

1. Open the `Todo.svelte` file, and add the `lang='ts'` attribute.
2. Let's import the `TodoType`, and set the type of the `todo` prop. Replace the `export let todo` line with the following:

```ts
import type { TodoType } from "../types/todo.type";

export let todo: TodoType;
```

3. The first warning we get is TypeScript telling us to define the type of the `update()` function's `updatedTodo` variable. This can be a little tricky because `updatedTodo` contains only the attributes of the `todo` that have been updated. That means it's not a complete `todo` — it only has a subset of a `todo`'s properties.
 For these kinds of cases, TypeScript provides several utility types to make it easier to apply these common transformations. What we need right now is the `Partial<T>` utility, which allows us to represent all subsets of a given type. The partial utility returns a new type based on the type `T`, where every property of `T` is optional.
 We'll use it in the `update()` function — update yours like so:

```ts
function update(updatedTodo: Partial<TodoType>) {
  todo = { ...todo, ...updatedTodo }; // applies modifications to todo
  dispatch("update", todo); // emit update event
}
```

With this we are telling TypeScript that the `updatedTodo` variable will hold a subset of the `TodoType` properties.
4. Now svelte-check tells us that we have to define the type of our action function parameters:

```bash
./07-next-steps/src/components/Todo.svelte:45:24
Warn: Parameter 'node' implicitly has an 'any' type, but a better type may be inferred from usage. (ts)
  const focusOnInit = (node) => node && typeof node.focus === 'function' && node.focus()

./07-next-steps/src/components/Todo.svelte:47:28
Warn: Parameter 'node' implicitly has an 'any' type, but a better type may be inferred from usage. (ts)
  const focusEditButton = (node) => editButtonPressed && node.focus()
```

We just have to define the node variable to be of type `HTMLElement`. In the two lines indicated above, replace the first instance of `node` with `node: HTMLElement`.
### actions.js

Next we'll take care of the `actions.js` file.

1. Rename it to `actions.ts` and add the type of the node parameter. It should end up looking like this:

```ts
// actions.ts
export function selectOnFocus(node: HTMLInputElement) {
  if (node && typeof node.select === "function") {
    // make sure node is defined and has a select() method
    const onFocus = () => node.select(); // event handler
    node.addEventListener("focus", onFocus); // when node gets focus call onFocus()
    return {
      destroy: () => node.removeEventListener("focus", onFocus), // this will be executed when the node is removed from the DOM
    };
  }
}
```

2. Now update `Todo.svelte` and `NewTodo.svelte` where we import the actions file. Remember that imports in TypeScript don't include the file extension. In each case it should end up like this:

```js
import { selectOnFocus } from "../actions";
```

### Migrating the stores to TypeScript

Now we have to migrate the `stores.js` and `localStore.js` files to TypeScript.

Tip: the script `npm run check`, which uses the `svelte-check` tool, will only check our application's `.svelte` files. If you want to also check the `.ts` files, you can run `npm run check && npx tsc --noemit`, which tells the TypeScript compiler to check for errors without generating the `.js` output files. You could even add a script to your `package.json` file that runs that command.

We'll start with `stores.js`.

1. Rename the file to `stores.ts`.
2. Set the type of our `initialTodos` array to `TodoType[]`. This is how the contents will end up:

```ts
// stores.ts
import { writable } from "svelte/store";
import { localStore } from "./localStore.js";
import type { TodoType } from "./types/todo.type";

export const alert = writable("Welcome to the To-Do list app!");

const initialTodos: TodoType[] = [
  { id: 1, name: "Visit MDN web docs", completed: true },
  { id: 2, name: "Complete the Svelte Tutorial", completed: false },
];

export const todos = localStore("mdn-svelte-todo", initialTodos);
```

3. Remember to update the `import` statements in `App.svelte`, `Alert.svelte`, and `Todos.svelte`. Just remove the `.js` extension, like this:

```js
import { todos } from "../stores";
```

Now onto `localStore.js`.

Update the `import` statement in `stores.ts` like so:

```js
import { localStore } from "./localStore";
```

1. Start by renaming the file to `localStore.ts`.
2. TypeScript is telling us to specify the type of the `key`, `initial`, and `value` variables. The first one is easy: the key of our local web storage should be a string.
 But `initial` and `value` should be any object that could be converted to a valid JSON string with the `JSON.stringify` method, meaning any JavaScript object with a couple limitations: for example, `undefined`, functions, and symbols are not valid JSON values.
 So we'll create the type `JsonValue` to specify these conditions.
 Create the file `json.type.ts` in the `types` folder.
3. Give it the following content:

```ts
export type JsonValue =
  | string
  | number
  | boolean
  | null
  | JsonValue[]
  | { [key: string]: JsonValue };
```

The `|` operator lets us declare variables that could store values of two or more types. A `JsonValue` could be a string, a number, a boolean, and so on. In this case we are also making use of recursive types to specify that a `JsonValue` can have an array of `JsonValue` and also an object with properties of type `JsonValue`.
4. We will import our `JsonValue` type and use it accordingly. Update your `localStore.ts` file like this:

```ts
// localStore.ts
import { writable } from "svelte/store";

import type { JsonValue } from "./types/json.type";

export const localStore = (key: string, initial: JsonValue) => {
  // receives the key of the local storage and an initial value

  const toString = (value: JsonValue) => JSON.stringify(value, null, 2); // helper function
  const toObj = JSON.parse; // helper function

  if (localStorage.getItem(key) === null) {
    // item not present in local storage
    localStorage.setItem(key, toString(initial)); // initialize local storage with initial value
  }

  const saved = toObj(localStorage.getItem(key)); // convert to object

  const { subscribe, set, update } = writable(saved); // create the underlying writable store

  return {
    subscribe,
    set: (value: JsonValue) => {
      localStorage.setItem(key, toString(value)); // save also to local storage as a string
      return set(value);
    },
    update,
  };
};
```

Now if we try to create a `localStore` with something that cannot be converted to JSON via `JSON.stringify()`, for example an object with a function as a property, VS Code/`validate` will complain about it:

![VS Code showing an error with using our store — it fails when trying to set a local storage value to something incompatible with JSON stringify](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/11-vscode-invalid-store.png)

And best of all, it will even work with the `$store` auto-subscription syntax. If we try to save an invalid value to our `todos` store using the `$store` syntax, like this:

```svelte
<!-- App.svelte -->
<script lang="ts">
 import Todos from "./components/Todos.svelte";
 import Alert from "./components/Alert.svelte";

 import { todos } from "./stores";

 // this is invalid, the content cannot be converted to JSON using JSON.stringify
 $todos = { handler: () => {} };
</script>
```

The check script will report the following error:

```bash
> npm run check

Getting Svelte diagnostics...
====================================

./svelte-todo-typescript/src/App.svelte:8:12
Error: Argument of type '{ handler: () => void; }' is not assignable to parameter of type 'JsonValue'.
  Types of property 'handler' are incompatible.
    Type '() => void' is not assignable to type 'JsonValue'.
      Type '() => void' is not assignable to type '{ [key: string]: JsonValue; }'.
        Index signature is missing in type '() => void'. (ts)
 $todos = { handler: () => {} }
```

This is another example of how specifying types can make our code more robust and help us catch more bugs before they get into production.

And that's it. We've converted our whole application to use TypeScript.

Bulletproofing our stores with Generics
---------------------------------------

Our stores have already been ported to TypeScript, but we can do better. We shouldn't need to store any kind of value — we know that the alert store should contain string messages, and the to-dos store should contain an array of `TodoType`, etc. We can let TypeScript enforce this using TypeScript Generics. Let's find out more.

### Understanding TypeScript generics

Generics allow us to create reusable code components that work with a variety of types instead of a single type. They can be applied to interfaces, classes, and functions. Generic types are passed as parameters using a special syntax: they are specified between angle brackets, and by convention are denoted with an upper-cased single char letter. Generic types allows us to capture the types provided by the user to be used later.

Let's see a quick example, a simple `Stack` class that lets us `push` and `pop` elements, like this:

```ts
export class Stack {
  private elements = [];

  push = (element) => this.elements.push(element);

  pop() {
    if (this.elements.length === 0) throw new Error("The stack is empty!");
    return this.elements.pop();
  }
}
```

In this case `elements` is an array of type `any`, and accordingly the `push()` and `pop()` methods both receive and return a variable of type `any`. So it's perfectly valid to do something like the following:

```js
const anyStack = new Stack();

anyStack.push(1);
anyStack.push("hello");
```

But what if we wanted to have a `Stack` that would only work with type `string`? We could do the following:

```ts
export class StringStack {
  private elements: string[] = [];

  push = (element: string) => this.elements.push(element);

  pop(): string {
    if (this.elements.length === 0) throw new Error("The stack is empty!");
    return this.elements.pop();
  }
}
```

That would work. But if we wanted to work with numbers, we would then have to duplicate our code and create a `NumberStack` class. And how could we handle a stack of types we don't know yet, and that should be defined by the consumer?

To solve all these problems, we can use generics.

This is our `Stack` class reimplemented using generics:

```ts
export class Stack<T> {
  private elements: T[] = [];

  push = (element: T): number => this.elements.push(element);

  pop(): T {
    if (this.elements.length === 0) throw new Error("The stack is empty!");
    return this.elements.pop();
  }
}
```

We define a generic type `T` and then use it like we would normally use a specific type. Now elements is an array of type `T`, and `push()` and `pop()` both receive and return a variable of type `T`.

This is how we would use our generic `Stack`:

```ts
const numberStack = new Stack<number>();
numberStack.push(1);
```

Now TypeScript knows that our stack can only accept numbers, and will issue an error if we try to push anything else:

![Argument of type hello is not assignable to parameter of type number](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/12-vscode-generic-stack-error.png)

TypeScript can also infer generic types by its usage. Generics also support default values and constraints.

Generics are a powerful feature that allows our code to abstract away from the specific types being used, making it more reusable and generic without giving up on type safety. To learn more about it, check out the TypeScript Introduction to Generics.

### Using Svelte stores with generics

Svelte stores support generics out of the box. And, because of generic type inference, we can take advantage of it without even touching our code.

If you open the file `Todos.svelte` and assign a `number` type to our `$alert` store, you'll get the following error:

![Todo Type object property complete should be completed](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/13-vscode-generic-alert-error.png)

That's because when we defined our alert store in the `stores.ts` file with:

```js
export const alert = writable("Welcome to the To-Do list app!");
```

TypeScript inferred the generic type to be `string`. If we wanted to be explicit about it, we could do the following:

```ts
export const alert = writable<string>("Welcome to the To-Do list app!");
```

Now we'll make our `localStore` store support generics. Remember that we defined the `JsonValue` type to prevent the usage of our `localStore` store with values that cannot be persisted using `JSON.stringify()`. Now we want the consumers of `localStore` to be able to specify the type of data to persist, but instead of working with any type, they should comply with the `JsonValue` type. We'll specify that with a Generic constraint, like this:

```ts
export const localStore = <T extends JsonValue>(key: string, initial: T)
```

We define a generic type `T` and specify that it must be compatible with the `JsonValue` type. Then we'll use the `T` type appropriately.

Our `localStore.ts` file will end up like this — try the new code now in your version:

```ts
// localStore.ts
import { writable } from "svelte/store";

import type { JsonValue } from "./types/json.type";

export const localStore = <T extends JsonValue>(key: string, initial: T) => {
  // receives the key of the local storage and an initial value

  const toString = (value: T) => JSON.stringify(value, null, 2); // helper function
  const toObj = JSON.parse; // helper function

  if (localStorage.getItem(key) === null) {
    // item not present in local storage
    localStorage.setItem(key, toString(initial)); // initialize local storage with initial value
  }

  const saved = toObj(localStorage.getItem(key)); // convert to object

  const { subscribe, set, update } = writable<T>(saved); // create the underlying writable store

  return {
    subscribe,
    set: (value: T) => {
      localStorage.setItem(key, toString(value)); // save also to local storage as a string
      return set(value);
    },
    update,
  };
};
```

And thanks to generic type inference, TypeScript already knows that our `$todos` store should contain an array of `TodoType`:

![Todo Type object property complete should be completed](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript/14-vscode-generic-localstore-error.png)

Once again, if we wanted to be explicit about it, we could do so in the `stores.ts` file like this:

```ts
const initialTodos: TodoType[] = [
  { id: 1, name: "Visit MDN web docs", completed: true },
  { id: 2, name: "Complete the Svelte Tutorial", completed: false },
];

export const todos = localStore<TodoType[]>("mdn-svelte-todo", initialTodos);
```

That will do for our brief tour of TypeScript Generics.

The code so far
---------------

### Git

To see the state of the code as it should be at the end of this article, access your copy of our repo like this:

```bash
cd mdn-svelte-tutorial/08-next-steps
```

Or directly download the folder's content:

```bash
npx degit opensas/mdn-svelte-tutorial/08-next-steps
```

Remember to run `npm install && npm run dev` to start your app in development mode.

### REPL

As we said earlier, TypeScript is not yet available in the REPL.

Summary
-------

In this article we took our to-do list application and ported it to TypeScript.

We first learnt about TypeScript and what advantages it can bring us. Then we saw how to create a new Svelte project with TypeScript support. We also saw how to convert an existing Svelte project to use TypeScript — our to-do list app.

We saw how to work with Visual Studio Code and the Svelte extension to get features like type checking and auto-completion. We also used the `svelte-check` tool to inspect TypeScript issues from the command line.

In the next article we will learn how to compile and deploy our app to production. We will also see which resources are available online to go further with learning Svelte.

* Previous
* Overview: Client-side JavaScript frameworks
* Next


Ember interactivity: Events, classes and state - Learn web development

Ember interactivity: Events, classes and state
==============================================

* Previous
* Overview: Client-side JavaScript frameworks
* Next

At this point we'll start adding some interactivity to our app, providing the ability to add and display new todo items. Along the way, we'll look at using events in Ember, creating component classes to contain JavaScript code to control interactive features, and setting up a service to keep track of the data state of our app.

|  |  |
| --- | --- |
| Prerequisites: |
 At minimum, it is recommended that you are familiar with the core
 HTML,
 CSS, and
 JavaScript languages, and
 have knowledge of the
 terminal/command line.

 A deeper understanding of modern JavaScript features (such as classes,
 modules, etc.), will be extremely beneficial, as Ember makes heavy use
 of them.
  |
| Objective: |
 To learn how to create component classes and use events to control
 interactivity, and keep track of app state using a service.
  |

Adding Interactivity
--------------------

Now we've got a refactored componentized version of our todo app, lets walk through how we can add the interactivity we need to make the app functional.

When beginning to think about interactivity, it's good to declare what each component's goals and responsibilities are. In the below sections we'll do this for each component, and then walk you through how the functionality can be implemented.

Creating todos
--------------

For our card-header / todo input, we'll want to be able to submit our typed in todo task when we press the `Enter` key and have it appear in the todos list.

We want to be able to capture the text typed into the input. We do this so that our JavaScript code knows what we typed in, and we can save our todo and pass that text along to the todo list component to display.

We can capture the `keydown` event via the on modifier, which is just Ember syntactic sugar around `addEventListener` and `removeEventListener` (see this explanation if needed).

Add the new line shown below to your `header.hbs` file:

```hbs
<input
  class='new-todo'
  aria-label='What needs to be done?'
  placeholder='What needs to be done?'
  autofocus
  {{on 'keydown' this.onKeyDown}}
>
```

This new attribute is inside double curly braces, which tells you it is part of Ember's dynamic templating syntax. The first argument passed to `on` is the type of event to respond to (`keydown`), and the last argument is the event handler — the code that will run in response to the `keydown` event firing. As you may expect from dealing with vanilla JavaScript objects, the `this` keyword refers to the "context" or "scope" of the component. One component's `this` will be different from another component's `this`.

We can define what is available inside `this` by generating a component class to go along with your component. This is a vanilla JavaScript class and has no special meaning to Ember, other than *extending* from the `Component` super-class.

To create a header class to go with your header component, type this in to your terminal:

```bash
ember generate component-class header
```

This will create the following empty class file — `todomvc/app/components/header.js`:

```js
import Component from "@glimmer/component";

export default class HeaderComponent extends Component {}
```

Inside this file we will implement the event handler code. Update the content to the following:

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

export default class HeaderComponent extends Component {
  @action
  onKeyDown({ target, key }) {
    let text = target.value.trim();
    let hasValue = Boolean(text);

    if (key === "Enter" && hasValue) {
      alert(text);

      target.value = "";
    }
  }
}
```

The `@action` decorator is the only Ember-specific code here (aside from extending from the `Component` superclass, and the Ember-specific items we are importing using JavaScript module syntax) — the rest of the file is vanilla JavaScript, and would work in any application. The `@action` decorator declares that the function is an "action", meaning it's a type of function that will be invoked from an event that occurred in the template. `@action` also binds the `this` of the function to the class instance.

**Note:** A decorator is basically a wrapper function, which wraps and calls other functions or properties, providing additional functionality along the way. For example, the `@tracked` decorator (see slightly later on) runs code it is applied to, but additionally tracks it and automatically updates the app when values change. Read JavaScript Decorators: What They Are and When to Use Them for more general information on decorators.

Coming back to our browser tab with the app running, we can type whatever we want, and when we hit `Enter` we'll be greeted with an alert message telling us exactly what we typed.

![the initial placeholder state of the add function, showing the text entered into the input elements being alerted back to you.](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state/todos-hello-there-alert.png)

With the interactivity of the header input out of the way, we need a place to store todos so that other components can access them.

Storing Todos with a service
----------------------------

Ember has built-in application-level **state** management that we can use to manage the storage of our todos and allow each of our components to access data from that application-level state. Ember calls these constructs Services, and they live for the entire lifetime of the page (a page refresh will clear them; persisting the data for longer is beyond the scope of this tutorial).

Run this terminal command to generate a service for us to store our todo-list data in:

```bash
ember generate service todo-data
```

This should give you a terminal output like so:
```

installing service
  create app/services/todo-data.js
installing service-test
  create tests/unit/services/todo-data-test.js
```

This creates a `todo-data.js` file inside the `todomvc/app/services` directory to contain our service, which initially contains an import statement and an empty class:

```js
import Service from "@ember/service";

export default class TodoDataService extends Service {}
```

First of all, we want to define *what a todo is*. We know that we want to track both the text of a todo, and whether or not it is completed.

Add the following `import` statement below the existing one:

```js
import { tracked } from "@glimmer/tracking";
```

Now add the following class below the previous line you added:

```js
class Todo {
  @tracked text = "";
  @tracked isCompleted = false;

  constructor(text) {
    this.text = text;
  }
}
```

This class represents a todo — it contains a `@tracked text` property containing the text of the todo, and a `@tracked isCompleted` property that specifies whether the todo has been completed or not. When instantiated, a `Todo` object will have an initial `text` value equal to the text given to it when created (see below), and an `isCompleted` value of `false`. The only Ember-specific part of this class is the `@tracked` decorator — this hooks in to the reactivity system and allows Ember to update what you're seeing in your app automatically if the tracked properties change. More information on tracked can be found here.

Now it's time to add to the body of the service.

First add another `import` statement below the previous one, to make actions available inside the service:

```js
import { action } from "@ember/object";
```

Update the existing `export default class TodoDataService extends Service { }` block as follows:

```js
export default class TodoDataService extends Service {
  @tracked todos = [];

  @action
  add(text) {
    let newTodo = new Todo(text);

    this.todos.pushObject(newTodo);
  }
}
```

Here, the `todos` property on the service will maintain our list of todos contained inside an array, and we'll mark it with `@tracked`, because when the value of `todos` is updated we want the UI to update as well.

 And just like before, the `add()` function that will be called from the template gets annotated with the `@action` decorator to bind it to the class instance.
 While this may look like familiar JavaScript, you may notice that we're calling the method `pushObject()` on our `todos` array.
 This is because Ember extends JavaScript's Array prototype by default, giving us convenient methods to ensure Ember's tracking system knows about these changes.
 There are dozens of these methods, including `pushObjects()`, `insertAt()`, or `popObject()`, which can be used with any type (not just Objects).
 Ember's ArrayProxy also gives us more handy methods, like `isAny()`, `findBy()`, and `filterBy()` to make life easier.

Using the service from our header component
-------------------------------------------

Now that we've defined a way to add todos, we can interact with this service from the `header.js` input component to actually start adding them.

First of all, the service needs to be injected into the template via the `@inject` decorator, which we'll rename to `@service` for semantic clarity. To do this, add the following `import` line to `header.js`, beneath the two existing `import` lines:

```js
import { inject as service } from "@ember/service";
```

With this import in place, we can now make the `todo-data` service available inside the `HeaderComponent` class via the `todos` object, using the `@service` decorator. Add the following line just below the opening `export…` line:

```js
@service('todo-data') todos;
```

Now the placeholder `alert(text);` line can be replaced with a call to our new `add()` function. Replace it with the following:

```js
this.todos.add(text);
```

If we try this out in the todo app in our browser (`npm start`, go to `localhost:4200`), it will look like nothing happens after hitting the `Enter` key (although the fact that the app builds without any errors is a good sign). Using the Ember Inspector however, we can see that our todo was added:

![The app being shown in the Ember inspector, to prove that added todos are being stored by the service, even if they are not being displayed in the UI yet](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state/todos-in-ember-inspector.gif)

Displaying our todos
--------------------

Now that we know that we can create todos, there needs to be a way to swap out our static "Buy Movie Tickets" todos with the todos we're actually creating. In the `TodoList` component, we'll want to get the todos out of the service, and render a `Todo` component for each todo.

In order to retrieve the todos from the service, our `TodoList` component first needs a backing component class to contain this functionality. Press `Ctrl` + `C` to stop the development server, and enter the following terminal command:

```bash
ember generate component-class todo-list
```

This generates the new component class `todomvc/app/components/todo-list.js`.

Populate this file with the following code, which exposes the `todo-data` service, via the `todos` property, to our template. This makes it accessible via `this.todos` inside both the class and the template:

```js
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";

export default class TodoListComponent extends Component {
  @service("todo-data") todos;
}
```

One issue here is that our service is called `todos`, but the list of todos is also called `todos`, so currently we would access the data using `this.todos.todos`. This is not intuitive, so we'll add a getter to the `todos` service called `all`, which will represent all todos.

To do this, go back to your `todo-data.js` file and add the following below the `@tracked todos = [];` line:

```js
get all() {
  return this.todos;
}
```

Now we can access the data using `this.todos.all`, which is much more intuitive. To put this in action, go to your `todo-list.hbs` component, and replace the static component calls:

```hbs
<Todo />
<Todo />
```

With a dynamic `#each` block (which is basically syntactic sugar over the top of JavaScript's `forEach()`) that creates a `<Todo />` component for each todo available in the list of todos returned by the service's `all()` getter:

```hbs
{{#each this.todos.all as |todo|}}
<Todo @todo={{todo}} />
{{/each}}
```

Another way to look at this:

* `this` — the rendering context / component instance.
* `todos` — a property on `this`, which we defined in the `todo-list.js` component using `@service('todo-data') todos;`. This is a reference to the `todo-data` service, allowing us to interact with the service instance directly.
* `all` — a getter on the `todo-data` service that returns all the todos.

Try starting the server again and navigating to our app, and you'll find that it works! Well, sort of. Whenever you enter a new Todo item, a new list item appears below the text input, but unfortunately it always says "Buy Movie Tickets".

This is because the text label inside each list item is hardcoded to that text, as seen in `todo.hbs`:

```hbs
<label>Buy Movie Tickets</label>
```

Update this line to use the Argument `@todo` — which will represent the Todo that we passed in to this component when it was invoked in `todo-list.hbs`, in the line `<Todo @todo={{todo}} />`:

```hbs
<label>{{@todo.text}}</label>
```

OK, try it again. You should find that now the text submitted in the `<input>` is properly reflected in the UI:

![The app being shown in its final state of this article, with entered todo items being shown in the UI](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state/todos-being-appended-with-correct-text.gif)

Summary
-------

OK, so that's great progress for now. We can now add todo items to our app, and the state of the data is tracked using our service. Next we'll move on to getting our footer functionality working, including the todo counter, and look at conditional rendering, including correctly styling todos when they've been checked. We'll also wire up our "Clear completed" button.

* Previous
* Overview: Client-side JavaScript frameworks
* Next


Ember resources and troubleshooting - Learn web development

Ember resources and troubleshooting
===================================

* Previous
* Overview: Client-side JavaScript frameworks
* Next

Our final Ember article provides you with a list of resources that you can use to go further in your learning, plus some useful troubleshooting and other information.

|  |  |
| --- | --- |
| Prerequisites: |
 At minimum, it is recommended that you are familiar with the core
 HTML,
 CSS, and
 JavaScript languages, and
 have knowledge of the
 terminal/command line.

 A deeper understanding of modern JavaScript features (such as classes,
 modules, etc.), will be extremely beneficial, as Ember makes heavy use
 of them.
  |
| Objective: | To provide further resource links and troubleshooting information. |

Further resources
-----------------

* Ember.JS Guides
    + Tutorial: Super Rentals
* Ember.JS API Documentation
* Ember.JS Discord Server — a forum/chat server where you can meet the Ember community, ask for help, and help others!
General troubleshooting, gotchas, and misconceptions
----------------------------------------------------

This is nowhere near an extensive list, but it is a list of things that came up around the time of writing (latest update, May 2020).

### How do I debug what's going on in the framework?

For *framework-specific* things, there is the ember-inspector add-on, which allows inspection of:

* Routes & Controllers
* Components
* Services
* Promises
* Data (i.e., from a remote API — from ember-data, by default)
* Deprecation Information
* Render Performance

 For general JavaScript debugging, check out our guides on JavaScript Debugging
 as well as interacting with the browser's other debugging tools. In any default Ember
 project, there will be two main JavaScript files, `vendor.js` and `{app-name}.js`. Both of
 these files are generated with sourcemaps, so when you open the `vendor.js` or `{app-name}.js` to search for relevant code, when a debugger is placed, the sourcemap will be loaded and the breakpoint will be placed in the pre-transpiled code for easier correlation to your project code.

For more information on sourcemaps, why they're needed, and what the ember-cli does with them, see the Advanced Use: Asset Compilation guide. Note that sourcemaps are enabled by default.

### `ember-data` comes pre-installed; do I need it?

 Not at all. While `ember-data` solves *the most common problems* that any app dealing with
 data will run in to, it is possible to roll your own front-end data client. A common
 alternative is to any fully-featured front-end data client is The Fetch API.

Using the design patterns provided by the framework, a `Route` using `fetch()` would look something like this:

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

export default class MyRoute extends Route {
  async model() {
    let response = await fetch("some/url/to/json/data");
    let json = await response.json();

    return {
      data: json,
    };
  }
}
```

See more information on specifying the `Route`'s model here.

### Why can't I just use JavaScript?

 This is the *most* common question Ember folks hear from people who have previous
 experience with React. While it is technically possible to use JSX, or any
 other form of DOM creation, there has yet to be anything as robust as Ember's
 templating system. The intentional minimalism forces certain decisions, and allows
 for more consistent code, while keeping the template more structural rather than having them filled with bespoke logic.

See also: ReactiveConf 2017: Secrets of the Glimmer VM

### What is the state of the `mut` helper?

`mut` was not covered in this tutorial and is really baggage from a transitional time when Ember was moving from two-way bound data to the more common and easier-to-reason-about one-way bound data flow. It could be thought of as a build-time transform that wraps its argument with a setter function.

More concretely, using `mut` allows for template-only settings functions to be declared:

```hbs
<Checkbox
  @value={{this.someData}}
  @onToggle={{fn (mut this.someData) (not this.someData)}}
/>
```

Whereas, without `mut`, a component class would be needed:

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

export default class Example extends Component {
  @tracked someData = false;

  @action
  setData(newValue) {
    this.someData = newValue;
  }
}
```

Which would then be called in the template like so:

```hbs
<Checkbox @data={{this.someData}} @onChange={{this.setData}} />
```

Due to the conciseness of using `mut`, it may be desirable to reach for it. However, `mut` has unnatural semantics and has caused much confusion over the term of its existence.

 There have been a couple of new ideas put together into the form of addons that use the public APIs, `ember-set-helper` and `ember-box`. Both of these try to solve the problems of `mut`
 by introducing more obvious / "less magic" concepts, avoiding build-time transforms and
 implicit Glimmer VM behavior.

With `ember-set-helper`:

```hbs
<Checkbox @value={{this.someData}} @onToggle={{set this "someData" (not
this.someData)}} />
```

With `ember-box`:

```hbs
{{#let (box this.someData) as |someData|}}
  <Checkbox
    @value={{unwrap someData}}
    @onToggle={{update someData (not this.someData)}}
  />
{{/let}}
```

Note that none of these solutions are particularly common among members of the community, and as a whole, people are still trying to figure out an ergonomic and simple API for setting data in a template-only context, without backing JS.

### What is the purpose of Controllers?

Controllers are Singletons, which may help manage the rendering context of the
 active route. On the surface, they function much the same as the backing JavaScript of a Component. Controllers are (as of January 2020), the only way to manage URL Query Params.

 Ideally, controllers should be fairly light in their responsibilities, delegating to Components
 and Services where possible.

### What is the purpose of Routes?

 A Route represents part of the URL when a user navigates from place to place in the app.
 A Route has only a couple responsibilities:

* Load the *minimally required data* to render the route (or view-sub-tree).
* Gate access to the route and redirect if needed.
* Handle loading and error states from the minimally required data.

A Route only has 3 lifecycle hooks, all of which are optional:

* `beforeModel` — gate access to the route.
* `model` — where data is loaded.
* `afterModel` — verify access.

Last, a Route has the ability to handle common events resulting from configuring the `model`:

* `loading` — what to do when the `model` hook is loading.
* `error` — what to do when an error is thrown from `model`.

Both `loading` and `error` can render default templates as well as customized templates defined elsewhere in the application, unifying loading/error states.

More information on everything a Route can do is found in the API documentation.

* Previous
* Overview: Client-side JavaScript frameworks
* Next


Getting started with Angular - Learn web development

Getting started with Angular
============================

* Previous
* Overview: Client-side JavaScript frameworks
* Next

It is now time to look at Google's Angular framework, another popular option that you'll come across often. In this article we look at what Angular has to offer, install the prerequisites and set up a sample app, and look at Angular's basic architecture.

|  |  |
| --- | --- |
| Prerequisites: |
 Familiarity with the core HTML,
 CSS, and
 JavaScript languages,
 knowledge of the
 terminal/command line.
  |
| Objective: |
 To setup a local Angular development environment, create a starter app,
 and understand the basics of how it works.
  |

What is Angular?
----------------

Angular is a development platform, built on TypeScript. As a platform, Angular includes:

* A component-based framework for building scalable web applications
* A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more
* A suite of developer tools to help you develop, build, test, and update your code

When you build applications with Angular, you're taking advantage of a platform that can scale from single-developer projects to enterprise-level applications. Angular is designed to make updating as easy as possible, so you can take advantage of the latest developments with a minimum of effort. Best of all, the Angular ecosystem consists of a diverse group of over 1.7 million developers, library authors, and content creators.

Before you start exploring the Angular platform, you should know about the Angular CLI. The Angular CLI is the fastest, easiest, and recommended way to develop Angular applications. The Angular CLI makes a number of tasks easy. Here are some examples:

|  |  |
| --- | --- |
| `ng build` | Compiles an Angular app into an output directory. |
| `ng serve` | Builds and serves your application, rebuilding on file changes. |
| `ng generate` | Generates or modifies files based on a schematic. |
| `ng test` | Runs unit tests on a given project. |
| `ng e2e` | Builds and serves an Angular application, then runs end-to-end tests. |

You'll find the Angular CLI to be a valuable tool for building out your applications.

What you'll build
-----------------

This tutorial series guides you through building a to-do list application. Via this application you'll learn how to use Angular to manage, edit, add, delete, and filter items.

Prerequisites
-------------

To install Angular on your local system, you need the following:

* **Node.js**
 Angular requires a current, active LTS, or maintenance LTS version of Node.js. For information about specific version requirements, see the `engines` key in the package.json file.
 For more information on installing Node.js, see nodejs.org.
 If you are unsure what version of Node.js runs on your system, run `node -v` in a terminal window.
* **npm package manager**
 Angular, the Angular CLI, and Angular applications depend on npm packages for many features and functions.
 To download and install npm packages, you need an npm package manager.
 This guide uses the npm client command line interface, which is installed with `Node.js` by default.
 To check that you have the npm client installed, run `npm -v` in a terminal window.
Set up your application
-----------------------

 You can use the Angular CLI to run commands in your terminal for generating, building, testing, and deploying Angular applications.
 To install the Angular CLI, run the following command in your terminal:

```bash
npm install -g @angular/cli
```

 Angular CLI commands all start with `ng`, followed by what you'd like the CLI to do.
 In the Desktop directory, use the following `ng new` command to create a new application called `todo`:

```bash
ng new todo --routing=false --style=css
```

 The `ng new` command creates a minimal starter Angular application on your Desktop.
 The additional flags, `--routing` and `--style`, define how to handle navigation and styles in the application.
 This tutorial describes these features later in more detail.

If you are prompted to enforce stricter type checking, you can respond with yes.

Navigate into your new project with the following `cd` command:

```bash
cd todo
```

To run your `todo` application, use `ng serve`:

```bash
ng serve
```

When the CLI prompts you about analytics, answer `no`.

 In the browser, navigate to `http://localhost:4200/` to see your new starter application.
 If you change any of the source files, the application automatically reloads.

 While `ng serve` is running, you might want to open a second terminal tab or window in order to run commands.
 If at any point you would like to stop serving your application, press `Ctrl+c` while in the terminal.

Get familiar with your Angular application
------------------------------------------

 The application source files that this tutorial focuses on are in `src/app`.
 Key files that the CLI generates automatically include the following:

1. `app.module.ts`: Specifies the files that the application uses.
 This file acts as a central hub for the other files in your application.
2. `app.component.ts`: Also known as the class, contains the logic for the application's main page.
3. `app.component.html`: Contains the HTML for `AppComponent`. The contents of this file are also known as the template.
 The template determines the view or what you see in the browser.
4. `app.component.css`: Contains the styles for `AppComponent`. You use this file when you want to define styles that only apply to a specific component, as opposed to your application overall.

 A component in Angular is made up of three main parts—the template, styles, and the class.
 For example, `app.component.ts`, `app.component.html`, and `app.component.css` together constitute the `AppComponent`.
 This structure separates the logic, view, and styles so that the application is more maintainable and scalable.

In this way, you are using the best practices from the very beginning.

The Angular CLI also generates a file for component testing called `app.component.spec.ts`, but this tutorial doesn't go into testing, so you can ignore that file.

Whenever you generate a component, the CLI creates these four files in a directory with the name you specify.

The structure of an Angular application
---------------------------------------

 Angular is built with TypeScript.
 TypeScript is a superset of JavaScript meaning that any valid JavaScript is valid TypeScript.
 TypeScript offers typing and a more concise syntax than plain JavaScript, which gives you a tool for creating more maintainable code and minimizing bugs.

 Components are the building blocks of an Angular application.
 A component includes a TypeScript class that has a `@Component()` decorator.

### The decorator

You use the `@Component()` decorator to specify metadata (HTML template and styles) about a class.

### The class

 The class is where you put any logic your component needs.
 This code can include functions, event listeners, properties, and references to services to name a few.
 The class is in a file with a name such as `feature.component.ts`, where `feature` is the name of your component.
 So, you could have files with names such as `header.component.ts`, `signup.component.ts`, or `feed.component.ts`.
 You create a component with a `@Component()` decorator that has metadata that tells Angular where to find the HTML and CSS.
 A typical component is as follows:

```js
import { Component } from "@angular/core";

@Component({
  selector: "app-item",
  // the following metadata specifies the location of the other parts of the component
  templateUrl: "./item.component.html",
  styleUrls: ["./item.component.css"],
})
export class ItemComponent {
  // your code goes here
}
```

 This component is called `ItemComponent`, and its selector is `app-item`.
 You use a selector just like regular HTML tags by placing it within other templates.
 When a selector is in a template, the browser renders the template of that component whenever an instance of the selector is encountered.
 This tutorial guides you through creating two components and using one within the other.

**NOTE:** The name of the component above is `ItemComponent` which is also the name of the class. Why?
 The names are the same simply because a component is nothing but a class supplemented by a TypeScript decorator.

 Angular's component model offers strong encapsulation and an intuitive application structure.
 Components also make your application easier to unit test and can improve the overall readability of your code.

### The HTML template

 Every component has an HTML template that declares how that component renders.
 You can define this template either inline or by file path.

To refer to an external HTML file, use the `templateUrl` property:

```js
@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
})
export class AppComponent {
  // code goes here
}
```

To write inline HTML, use the `template` property and write your HTML within backticks:

```js
@Component({
  selector: "app-root",
  template: `<h1>Hi!</h1>`,
})
export class AppComponent {
  // code goes here
}
```

 Angular extends HTML with additional syntax that lets you insert dynamic values from your component.
 Angular automatically updates the rendered DOM when your component's state changes.
 One use of this feature is inserting dynamic text, as shown in the following example.

```html
<h1>{{ title }}</h1>
```

 The double curly braces instruct Angular to interpolate the contents within them.
 The value for `title` comes from the component class:

```js
import { Component } from "@angular/core";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  title = "To do application";
}
```

When the application loads the component and its template, the browser sees the following:

```html
<h1>To do application</h1>
```

### Styles

 A component can inherit global styles from the application's `styles.css` file and augment or override them with its own styles.
 You can write component-specific styles directly in the `@Component()` decorator or specify the path to a CSS file.

To include the styles directly in the component decorator, use the `styles` property:

```js
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styles: ['h1 { color: red; }']
})
```

Typically, a component uses styles in a separate file using the `styleUrls` property:

```js
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
```

With component-specific styles, you can organize your CSS so that it is easily maintainable and portable.

Summary
-------

That's it for your first introduction to Angular. At this point you should be set up and ready to build an Angular app, and have a basic understanding of how Angular works. In the next article we'll deepen that knowledge and start to build up the structure of our to-do list application.

* Previous
* Overview: Client-side JavaScript frameworks
* Next


Ember app structure and componentization - Learn web development

Ember app structure and componentization
========================================

* Previous
* Overview: Client-side JavaScript frameworks
* Next

In this article we'll get right on with planning out the structure of our TodoMVC Ember app, adding in the HTML for it, and then breaking that HTML structure into components.

|  |  |
| --- | --- |
| Prerequisites: |
 At minimum, it is recommended that you are familiar with the core
 HTML,
 CSS, and
 JavaScript languages, and
 have knowledge of the
 terminal/command line.

 A deeper understanding of modern JavaScript features (such as classes,
 modules, etc.), will be extremely beneficial, as Ember makes heavy use
 of them.
  |
| Objective: |
 To learn how to structure an Ember app, and then break that structure
 into components.
  |

Planning out the layout of the TodoMVC app
------------------------------------------

In the last article we set up a new Ember project, then added and configured our CSS styles. At this point we add some HTML, planning out the structure and semantics of our TodoMVC app.

The landing page HTML of our application is defined in `app/templates/application.hbs`. This already exists, and its contents currently look like so:

```hbs
{{!-- The following component displays Ember's default welcome message. --}}
<WelcomePage />
{{!-- Feel free to remove this! --}}

{{outlet}}
```

`<WelcomePage />` is a component provided by an Ember addon that renders the default welcome page we saw in the previous article, when we first navigated to our server at `localhost:4200`.

However, we don't want this. Instead, we want it to contain the TodoMVC app structure. To start with, delete the contents of `application.hbs` and replace them with the following:

```html
<section class="todoapp">
  <h1>todos</h1>
  <input
 class="new-todo"
 aria-label="What needs to be done?"
 placeholder="What needs to be done?"
 autofocus />
</section>
```

**Note:** `aria-label` provides a label for assistive technology to make use of — for example, for a screen reader to read out. This is useful in such cases where we have an `<input>` being used with no corresponding HTML text that could be turned into a label.

When you save `application.hbs`, the development server you started earlier will automatically rebuild the app and refresh the browser. The rendered output should now look like this:

![todo app rendered in the browser with only the new todo input field showing](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization/todos-initial-render.png)

It doesn't take too much effort to get our HTML looking like a fully-featured to-do list app. Update the `application.hbs` file again so its content looks like this:

```html
<section class="todoapp">
  <h1>todos</h1>
  <input
 class="new-todo"
 aria-label="What needs to be done?"
 placeholder="What needs to be done?"
 autofocus />

  <section class="main">
    <input id="mark-all-complete" class="toggle-all" type="checkbox" />
    <label for="mark-all-complete">Mark all as complete</label>

    <ul class="todo-list">
      <li>
        <div class="view">
          <input
 aria-label="Toggle the completion of this todo"
 class="toggle"
 type="checkbox" />
          <label>Buy Movie Tickets</label>
          <button
 type="button"
 class="destroy"
 title="Remove this todo"></button>
        </div>

        <input autofocus class="edit" value="Todo Text" />
      </li>

      <li>
        <div class="view">
          <input
 aria-label="Toggle the completion of this todo"
 class="toggle"
 type="checkbox" />
          <label>Go to Movie</label>
          <button
 type="button"
 class="destroy"
 title="Remove this todo"></button>
        </div>

        <input autofocus class="edit" value="Todo Text" />
      </li>
    </ul>
  </section>

  <footer class="footer">
    <span class="todo-count"> <strong>0</strong> todos left </span>

    <ul class="filters">
      <li>
        <a href="#">All</a>
        <a href="#">Active</a>
        <a href="#">Completed</a>
      </li>
    </ul>

    <button type="button" class="clear-completed">Clear Completed</button>
  </footer>
</section>
```

The rendered output should now be as follows:

![todo app rendered in the browser with new todo input field and existing todos showing, - buy movie tickets and go to movie](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization/todos-with-todo-items.png)

This looks pretty complete, but remember that this is only a static prototype. Now we need to break up our HTML code into dynamic components; later we'll turn it into a fully interactive app.

Looking at the code next to the rendered todo app, there are a number of ways we could decide how to break up the UI, but let's plan on splitting the HTML out into the following components:

![code screenshot annotated to show what parts of the code will go into which component](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization/todos-ui-component-breakdown.png)

The component groupings are as follows:

* The main input / "new-todo" (red in the image)
* The containing body of the todo list + the `mark-all-complete` button (purple in the image)
    + The `mark-all-complete button`, explicitly highlighted for reasons given below (yellow in the image)
    + Each todo is an individual component (green in the image)
* The footer (blue in the image)

Something odd to note is that the `mark-all-complete` checkbox (marked in yellow), while in the "main" section, is rendered next to the "new-todo" input. This is because the default CSS absolutely positions the checkbox + label with negative top and left values to move it next to the input, rather than it being inside the "main" section.

![todo app looked at through devtools](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization/todos-devtools-view.png)

Using the CLI to create our components for us
---------------------------------------------

So to represent our app, we want to create 4 components:

* Header
* List
* Individual Todo
* Footer

To create a component, we use the `ember generate component` command, followed by the name of the component. Let's create the header component first. To do so:

1. Stop the server running by going to the terminal and pressing `Ctrl` + `C`.
2. Enter the following command into your terminal:

```bash
ember generate component header
```

These will generate some new files, as shown in the resulting terminal output:
```

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

`header.hbs` is the template file where we'll include the HTML structure for just that component. Later on we'll add the required dynamic functionality such as data bindings, responding to user interaction, etc.

**Note:** The `header.js` file (shown as skipped) is for connection to a backing Glimmer Component Class, which we don't need for now, as they are for adding interactivity and state manipulation. By default, `generate component` generates template-only components, because in large applications, template-only components end up being the majority of the components.

`header-test.js` is for writing automated tests to ensure that our app continues to work over time as we upgrade, add features, refactor, etc. Testing is outside the scope of this tutorial, although generally testing should be implemented as you develop, rather than after, otherwise it tends to be forgotten about. If you're curious about testing, or why you would want to have automated tests, check out the official Ember tutorial on testing.

Before we start adding any component code, let's create the scaffolding for the other components. Enter the following lines into your terminal, one by one:

```bash
ember generate component todo-list
ember generate component todo
ember generate component footer
```

You'll now see the following inside your `todomvc/app/components` directory:

![the app components directory, showing the component template files we've created](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization/todos-components-directory.png)

Now that we have all of our component structure files, we can cut and paste the HTML for each component out of the `application.hbs` file and into each of those components, and then re-write the `application.hbs` to reflect our new abstractions.

1. The `header.hbs` file should be updated to contain the following:

```html
<input
 class="new-todo"
 aria-label="What needs to be done?"
 placeholder="What needs to be done?"
 autofocus />
```

2. `todo-list.hbs` should be updated to contain this chunk of code:

```html
<section class="main">
  <input id="mark-all-complete" class="toggle-all" type="checkbox" />
  <label for="mark-all-complete">Mark all as complete</label>

  <ul class="todo-list">
    <Todo />
    <Todo />
  </ul>
</section>
```

**Note:** The only non-HTML in this new `todo-list.hbs` is the `<Todo />` component invocation. In Ember, a component invocation is similar to declaring an HTML element, but the first letter starts with a capital letter, and the names are written in upper camel case, as you'll see with `<TodoList />` later on. The contents of the `todo.hbs` file below will replace `<Todo />` in the rendered page as our application loads.
3. Add the following into the `todo.hbs` file:

```html
<li>
  <div class="view">
    <input
 aria-label="Toggle the completion of this todo"
 class="toggle"
 type="checkbox" />
    <label>Buy Movie Tickets</label>
    <button type="button" class="destroy" title="Remove this todo"></button>
  </div>

  <input autofocus class="edit" value="Todo Text" />
</li>
```

4. `footer.hbs` should be updated to contain the following:

```html
<footer class="footer">
  <span class="todo-count"> <strong>0</strong> todos left </span>

  <ul class="filters">
    <li>
      <a href="#">All</a>
      <a href="#">Active</a>
      <a href="#">Completed</a>
    </li>
  </ul>

  <button type="button" class="clear-completed">Clear Completed</button>
</footer>
```

5. Finally, the contents of `application.hbs` should be updated so that they call the appropriate components, like so:

```hbs
<section class="todoapp">
  <h1>todos</h1>

  <Header />
  <TodoList />
  <Footer />
</section>
```

6. With these changes made, run `npm start` in your terminal again, then head over to `http://localhost:4200` to ensure that the todo app still looks as it did before the refactor.

![todo app rendered in the browser with new todo input field and existing todos showing, both saying buy movie tickets](/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization/todos-components-render.png)

Notice how the todo items both say "Buy Movie Tickets" — this is because the same component is being invoked twice, and the todo text is hardcoded into it. We'll look at showing different todo items in the next article!

Summary
-------

Great! Everything looks as it should. We have successfully refactored our HTML into components! In the next article we'll start looking into adding interactivity to our Ember application.

* Previous
* Overview: Client-side JavaScript frameworks
* Next