Search

#### Docs

- Guide
  - Overview
  - Setup
  - Learning Angular
  - Architecture
  - Displaying Data
  - User Input
  - Forms
  - Dependency Injection
  - Template Syntax
- Tutorial
  - Introduction
  - 0. The Starter App
  - 1. The Hero Editor
  - 2. Master/Detail
  - 3. Multiple Components
  - 4. Services
  - 5. Routing
  - 6. HTTP
- Advanced
  - Attribute Directives
  - Component Styles
  - Deployment
  - Hierarchical Injectors
  - HTTP Client
  - Lifecycle Hooks
  - Pipes
  - Router
  - Security
  - Structural Directives
  - Testing

#### Components

- Components Gallery

#### Resources

- Cheat Sheet
- Glossary
- FAQ
- Tools
- Community and Support

#### Reference

- Videos
- API Documentation

new_releases We're currently in the progress of migrating this site from Angular version 5.3.1 to 7.0.0. Package versions might be out-of-date or inconsistent! Visit the repo for more info.

# The Hero Editor 6.0

<?code-excerpt path-base=”examples/ng/doc/toh-1”?> In this part of the tutorial, you’ll modify the starter app to display information about a hero. Then you’ll add the ability to edit the hero’s data. When you’re done, the app should look like this live example (view source).

## Where you left off

Before you start writing code, let’s verify that you have the following structure. If not, you’ll need to go back and follow the setup instructions on the previous page.

- angular_tour_of_heroes
  - lib
    - app_component.dart
  - test
    - app_test.dart
  - web
    - index.html
    - main.dart
    - styles.css
  - analysis_options.yaml
  - pubspec.yaml

If the app isn’t running already, launch the app. As you make changes, keep it running by reloading the browser window.

## Show the hero

Make the following changes to `AppComponent`:

- Add a `hero` property for a hero named Windstorm.
- Add a `title` property initialized as shown below.
- Drop the `name` property.

<?code-excerpt “lib/app_component_1.dart (class)” replace=”/final.|var./[!$&!]/g” title?>

      class AppComponent {
        [!final title = 'Tour of Heroes';!]
        [!var hero = 'Windstorm';!]
      }

### Add multi-line template HTML

Update the template parameter in the `@Component` annotation with data bindings to these new properties:

<?code-excerpt “lib/app_component_1.dart (template)” title?>

      template: '''
        <h1>{{title}}</h1>
        <h2>{{hero}}</h2>
      ''',

open_in_browser Refresh the browser. The app displays the title and hero name.

The double curly braces are Angular’s interpolation syntax. These interpolation bindings present the component’s `title` and `hero` property values, as strings, inside the HTML header tags.

Read more about interpolation in the Displaying Data page.

### Create a Hero class

Create a `Hero` class with `id` and `name` properties and save it to the following new file:

<?code-excerpt “lib/hero.dart” title?>

      class Hero {
        final int id;
        String name;

        Hero(this.id, this.name);
      }

Make these changes to `app_component.dart`:

- Import `hero.dart`.
- In the `AppComponent` class, declare the type of `hero` to be `Hero`, and initialize it with a new `Hero` having an ID of `1` and the name “Windstorm”.

<?code-excerpt “lib/app_component_2.dart (import and class)” replace=”/import.|Hero(?= )|Hero.1./[!$&!]/g” title?>

      [!import 'hero.dart';!]
      // ···
      class AppComponent {
        final title = 'Tour of Heroes';
        [!Hero!] hero = [!Hero(1, 'Windstorm');!]
      }

Because you changed the hero from a string to an object, update the binding in the template to refer to the hero’s `name` property.

<?code-excerpt “lib/app_component_2.dart (template)” remove=”/’|div/” replace=”/.name/[!$&!]/g”?>

      <h1>{{title}}</h1>
      <h2>{{hero[!.name!]}}</h2>

open_in_browser Refresh the browser. The app continues to display the hero’s name.

### Show all hero properties

Update the template to show all of the hero’s properties: add a `<div>` for the hero’s `id` property and another `<div>` for the hero’s `name`.

<?code-excerpt “lib/app_component_2.dart (template)” remove=”’” title?>

      <h1>{{title}}</h1>
      <h2>{{hero.name}}</h2>
      <div><label>id: </label>{{hero.id}}</div>
      <div><label>name: </label>{{hero.name}}</div>

open_in_browser Refresh the browser. The app shows all the hero’s details.

## Enable editing the hero name

Users should be able to edit the hero name in an `<input>` textbox. The textbox should both display the hero’s `name` property and update that property as the user types.

You need a two-way binding between the `<input>` form element and the `hero.name` property.

### Use a two-way binding

Refactor the hero name in the template so it looks like this:

<?code-excerpt “lib/app_component.dart (template)” remove=”/h\d|id|’/”?>

      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name">
      </div>

`[(ngModel)]` is the Angular syntax to bind the `hero.name` property to the textbox. Data flows in both directions: from the property to the textbox, and from the textbox back to the property.

Read more about `ngModel` in the Forms and Template Syntax pages.

## Declare non-core directives

Unfortunately, immediately after this change, the app breaks!

### Template parse error

open_in_browser If you refresh the browser, the app won’t load. To know why, look at the webdev serve output. The template compiler doesn’t recognize `ngModel`, and issues a parse error for `AppComponent`:

      Error running TemplateGenerator for forms|lib/src/hero_form_component.dart.
      Error: Template parse errors:
      Can't bind to 'ngModel' since it isn't a known native property or known directive. Please fix typo or add to directives list.
      [(ngModel)]="hero.name"
      ^^^^^^^^^^^^^^^^^^^^^^^

### Update the pubspec

<?code-excerpt path-base=”examples/ng/doc”?>

The `ngforms` (also called `angular_forms`) library comes in its own package. Add the package to the pubspec dependencies:

<?code-excerpt “toh-0/pubspec.yaml” diff-with=”toh-1/pubspec.yaml” from=”dependencies” to=”ngforms”?>

    --- toh-0/pubspec.yaml
    +++ toh-1/pubspec.yaml
    @@ -8,2 +8,3 @@
     dependencies:
       ngdart: ^7.1.1
    +  ngforms: ^4.1.1

<?code-excerpt path-base=”examples/ng/doc/toh-1”?>

### Add @Component(directives: …)

Although `NgModel` is a valid Angular directive defined in the ngforms library, it isn’t available by default.

Before you can use any Angular directives in a template, you need to list them in the `directives` argument of your component’s `@Component` annotation. You can add directives individually, or for convenience you can add the formDirectives list (note the new import statement):

<?code-excerpt “lib/app_component.dart (directives)” replace=”/directives:.*/[!$&!]/g” title?>

      import 'package:ngforms/ngforms.dart';

      import 'hero.dart';

      @Component(
        selector: 'my-app',
        // ···
        [!directives: [formDirectives],!]
      )
      class AppComponent {
        // ···
      }

open_in_browser Refresh the browser and the app should work again. You can edit the hero’s name and see the changes reflected immediately in the `<h2>` heading above the textbox.

## The road you’ve travelled

Take stock of what you’ve built.

- The Tour of Heroes app uses the double curly braces of interpolation (a type of one-way data binding) to display the app title and properties of a `Hero` object.
- You wrote a multi-line template using Dart’s template strings to make the template readable.
- You added a two-way data binding to the `<input>` element using the built-in `ngModel` directive. This binding both displays the hero’s name and allows users to change it.
- You added formDirectives to the `directives` argument of the app’s `@Component` annotation so that Angular knows where `ngModel` is defined.

Your app should look like this live example (view source).

Here are the files that you created or modified:

## The road ahead

In the next tutorial page, you’ll build on the Tour of Heroes app to display a list of heroes. You’ll also allow the user to select heroes and display their details. You’ll learn more about how to retrieve lists and bind them to the template.