Build the app shown in this screenshot!

This code lab walks you through the process of building a simple web app with Dart and Angular 2. You don't need to know Dart, Angular, or web programming to complete this code lab, but we do assume you have some programming experience.

What you'll learn

How will you use this tutorial?

Read it through only Read it and complete the exercises

How would rate your experience with building Dart apps?

Novice Intermediate Proficient

In this step, you download any software that you need, and learn where to find the sample code.

Get Dart and Dartium

If you haven't already done so, get the Dart SDK and Dartium.

The Dart SDK download includes several Dart tools that you'll need. If you wish to run the Dart tools from the command line, add <path-to-the-SDK>/dart-sdk/bin to your path.

You will need Dartium to test your app during development.

Get IntelliJ IDEA, WebStorm, or a plugin

If you don't have a favorite editor already, try IntelliJ IDEA, community edition. You will install a plugin for Dart development. You can also download Dart plugins for other IDEs and editors.

If this is the first time you've used your IDE with Dart, you'll need to configure the plugin with the location of the Dart SDK and Dartium. See Configuring Dart support for instructions on configuring IntelliJ IDEA, community edition. The Dart Tools page has links where you can find more information about other plugins.

In this step, you create an Angular app, look at its code, and run the app in Dartium.

Create a simple Angular app

IntelliJ provides a set of templates for creating a variety of Dart apps. When you create a new app, you can start with one of the application templates, or you can start from scratch.

  1. Launch IntelliJ IDEA, community edition.
    A "Welcome to IntelliJ IDEA" screen appears.
  2. Choose Create New Project from the welcome screen. A New Project form appears.
  3. Select Dart from the list on the left.
  4. Check the Start Dartium in checked mode box.
  5. Check the Generate Sample content box.
  6. Select Angular 2 Web Application from the list of application templates.

    The screen should look similar to the following:
  7. Click Next.
  8. In the Project name input field, change the title from "untitled" to "pirate_badge" (without the quotes).
  9. Click Finish.

IntelliJ IDEA takes many seconds to analyze the sources and do other housekeeping. This only happens once. After that, you'll be able to do the usual things, like using F1 to get help on any method, class or field, or Command+B to navigate to a method's declaration, or Shift+F6 to refactor or rename.

What happened?

IntelliJ IDEA creates a pirate_badge directory and boilerplate files for a basic Angular app. It then runs pub get, Dart's package management tool, to download the packages that the app depends on. Finally, IntelliJ runs Dart's static analyzer over the code to look for errors and warnings.

Key information

  1. The messages view at the bottom of the screen reports the output from the pub commands.
  2. The editor view, on the right, opens with the contents of the web/index.html file.
  3. If the currently opened file passes analysis, a green checkmark displays in the upper right corner of the editor view. If the code fails analysis, a yellow box displays. Any errors or warnings are displayed in the messages view, or you can hover your cursor over the tick marks along the right edge of the editor view.

    IntelliJ might warn that my-app is an unknown HTML tag in index.html. You can ignore this warning.

What did you get?

Get familiar with the structure of a basic Angular app.

In the Project view, on the left, expand the pirate_badge folder. Then expand the lib and web folders to see the following:

For now, you can ignore some of these auto-created files. The following shows the files and directories referenced in this code lab:

pirate_badge/
  lib/
    app_component.dart
    app_component.html
  web/
    index.html
    main.dart
  pubspec.yaml

As you might expect, the lib directory contains library files. In an Angular app, component classes are generally created as library files. The web directory contains the main files for a web app. Double clicking a file opens that file in the editor view.

Key information

Review the code

Get familiar with the HTML and the Dart code for the skeleton version of the app. Double-click a filename in the project view to see its contents in the editor view. Double click the ellipsis ( ... ) highlighted in green to see the hidden text. You should the following code (all copyright notices are omitted here):

web/main.dart

import 'package:angular2/browser.dart';

import 'package:angular2/app_component.dart';

main() {
  bootstrap(AppComponent);
}

Key information

web/index.html

<!DOCTYPE html>
<html>
  <head>
    <title>pirate_badge</title>

    <script defer src="main.dart" type="application/dart"></script>
    <script defer src="packages/browser/dart.js"></script>
  </head>
  <body>
    <my-app>Loading...</my-app>
  </body>
</html>

Key information

lib/app_component.dart

import 'package:angular2/core.dart';

@Component(selector: 'my-app', templateUrl: 'app_component.html')
class AppComponent {}

Key information

lib/app_component.html

<h1>My First Angular 2 App</h1>

Key information

pubspec.yaml

name: pirate_badge
description: A Dart app that uses Angular 2
version: 0.0.1
environment:
  sdk: '>=1.13.0 <2.0.0'
dependencies:
  angular2: 2.0.0-beta.17
  browser: ^0.10.0
  dart_to_js_script_rewriter: ^1.0.1
transformers:
- angular2:
    platform_directives:
    - 'package:angular2/common.dart#COMMON_DIRECTIVES'
    platform_pipes:
    - 'package:angular2/common.dart#COMMON_PIPES'
    entry_points: web/main.dart
- dart_to_js_script_rewriter

Key information

Run the sample app

Run the app using Dartium.

In IntelliJ, double-click the web/index.html file to open the file in the editor view. Hover your mouse pointer over the code to show the browser icons bar, and click the Dartium icon on the far right.

IntelliJ launches the app in a Dartium window. You should see something like the following:

Problems?

Look in IntelliJ's window for possible errors. If that fails, look your browser's JavaScript console. In Dartium or Chrome, bring up the console using View > Developer > JavaScript Console.

Finally, if you still haven't found the problem check your code against the files in 1-skeleton.

In this step, you extend the basic Angular app with a badge component, which encapsulates the behavior and appearance of the pirate badge.

This is the hardest step in this code lab. By the end of this step, your app will display a snazzy name badge. The next steps, where you add interactivity, are easy and fun.

Add badge_component.html

  1. In IntelliJ's project view, right-click the lib directory and select New > File from the menu that pops up.
  2. Enter badge_component.html as the filename and click OK.

Key information

Edit badge_component.html

Enter the HTML for the name badge.

<div class="badge">
  <div class="greeting">Arrr! Me name is</div>
  <div class="name">{{badgeName}}</div>
</div>

Key information

Create a stylesheet for the badge component

Shiver me timbers!

The stylesheet is too long to include here, but we've provided one for you to copy and paste into your project.

  1. In IntelliJ's project view, right click the lib directory, and select New -> File from the menu that pops up.
  2. Enter badge_component.css as the filename and click OK.
  3. Paste the contents from badge_component.css into the newly created file.

Key information

Add badge_component.dart

  1. In IntelliJ's Project view, right-click the lib directory, and select New > Dart File from the menu that pops up.
  2. Enter badge_component as the filename and click OK.

Edit badge_component.dart

Import Angular's core library.

import 'package:angular2/core.dart';

Create a BadgeComponent class annotated with @Component. The class contains a name badge instance variable—replace "Sundar" with your name.

import 'package:angular2/core.dart';

@Component(
    selector: 'pirate-badge',
    templateUrl: 'badge_component.html',
    styleUrls: const ['badge_component.css'])
class BadgeComponent {
  String badgeName = 'Sundar';
}

Key information

Edit app_component.dart

Import the badge component.

import 'package:angular2/core.dart';

import 'badge_component.dart';

Key information

Add the text , directives: const [BadgeComponent] to the @Component annotation.

@Component(selector: 'my-app', templateUrl: 'app_component.html'
, directives: const [BadgeComponent])

Key information

Format the file.

To format the file that is currently open, right-click in the editor view and select Reformat with Dart Style from the menu that pops up. After formatting, the file should look like the following:

@Component(
    selector: 'my-app',
    templateUrl: 'app_component.html',
    directives: const [BadgeComponent])
class AppComponent {}

Key information

Edit app_component.html

Replace the contents of the HTML template:

<h1>Avast, Ye Pirates</h1>
<pirate-badge></pirate-badge>

Key information

Create a stylesheet for the app

  1. In IntelliJ's project view, right-click the web directory and select New > File from the menu that pops up.
  2. Enter styles.css in the dialog that opens and click OK. An empty styles.css file is created under web.
  3. Add the following CSS to the stylesheet:
body {
    background-color: #F8F8F8;
    font-family: 'Open Sans', sans-serif;
    font-size: 14px;
    font-weight: normal;
    line-height: 1.2em;
    margin: 15px;
}

Edit index.html

Change the title to "Avast, Ye Pirates".

  <head>
    <title>Avast, Ye Pirates</title>
    ...
  </head>

Add a reference to the stylesheet.

<head>
  <title>Avast, Ye Pirates</title>

  <link rel="stylesheet" href="styles.css">
  ...
</head>

Test it!

In IntelliJ, double-click the web/index.html file to open the file in the editor view. Hover your mouse pointer over the code to show the browser icons bar, and click the Dartium icon on the far right.

You should see a name badge with your name, or "Sundar" if you didn't change the name. Assuming your machine has the fonts specified in the CSS file, the badge should look similar to the following:

Next you will add some interactivity.

Problems?

Look in IntelliJ's window for possible errors. If that fails, look your browser's JavaScript console. In Dartium or Chrome, bring up the console using View > Developer > JavaScript Console.

Finally, if you still haven't found the problem check your code against the files in 2-blankbadge.

In this step, you add an input field. As the user types into the input field, the badge updates.

Edit badge_component.html

Add a div containing an input field to the top of the file:

<div class="widgets">
  <input (input)="updateBadge($event.target.value)"
         type="text" maxlength="15">
</div>

Key information

Edit badge_component.dart

Delete the hard coded badge name and add an event handler, updateBadge(), to the BadgeComponent class.

class BadgeComponent {
  String badgeName = '';
  void updateBadge(String inputName) {
    badgeName = inputName;
  }
}

Key information

Test it!

In IntelliJ, double-click the web/index.html file to open the file in the editor view. Hover your mouse pointer over the code to show the browser icons bar, and click the Dartium icon on the far right.

Type into the input field. The name badge updates to display what you've typed.

Problems?

Look in IntelliJ's window for possible errors. If that fails, look your browser's JavaScript console. In Dartium or Chrome, bring up the console using View > Developer > JavaScript Console.

Finally, if you still haven't found the problem check your code against the files in 3-inputnamebadge.

In this step, you add a button that's enabled when the input field is empty. When the user clicks the button, the app displays "Anne Bonney" on the badge.

Edit badge_component.html

Add a button to the widgets div.

<div class="widgets">
  <input ...>
  <button [disabled]="!isButtonEnabled" (click)="generateBadge()">
    {{buttonText}}
  </button>
</div>

Key information

Edit badge_component.dart

Add two variables to the BadgeComponent class.

class BadgeComponent {
  String badgeName = '';
  String buttonText = 'Aye! Gimme a name!';
  bool isButtonEnabled = true;
  ...
}

Key information

Add a generateBadge() function.

class BadgeComponent {
  ...
  bool isButtonEnabled = true;

  void generateBadge() {
    badgeName = 'Anne Bonney';
  }
  ...
}

Key information

Modify the updateBadge() function to toggle the button's state based on whether there is text in the input field.

class BadgeComponent {
  ...
  void updateBadge(String inputName) {
    badgeName = inputName;
    if (inputName.trim().isEmpty) {
      buttonText = 'Aye! Gimme a name!';
      isButtonEnabled = true;
    } else {
      buttonText = 'Arrr! Write yer name!';
      isButtonEnabled = false;
    }
  }

Key information

Test it!

In IntelliJ, double-click the web/index.html file to open the file in the editor view. Hover your mouse pointer over the code to show the browser icons bar, and click the Dartium icon on the far right.

Type in the input field. The name badge updates to display what you've typed, and the button is disabled. Remove the text from the input field and the button is enabled. Click the button. The name badge displays "Anne Bonney".

Problems?

Look in IntelliJ's window for possible errors. If that fails, look your browser's JavaScript console. In Dartium or Chrome, bring up the console using View > Developer > JavaScript Console.

Finally, if you still haven't found the problem check your code against the files in 4-buttonbadge.

A proper pirate name consists of a name and an appellation, such as "Margy the Fierce" or "Renée the Fighter". In this step, you learn about Angular's support for dependency injection as you add a service that returns a pirate name.

Create a class for the name service

  1. In IntelliJ's project view, right-click the lib directory and select New > Dart File from the menu that pops up.
  2. Enter "name_service" as the filename and click OK.

Edit name_service.dart

Add imports to the file.

import 'dart:math' show Random;

import 'package:angular2/core.dart';

Key information

Add a class declaration below the import and annotate it with @Injectable().

@Injectable()
class NameService {
}

Key information

Create a class-level Random object.

class NameService {
  static final Random _indexGen = new Random();
}

Key information

Create two lists within the class that provide a small collection of names and appellations to choose from.

class NameService {
  static final Random _indexGen = new Random();

  final List _names = [
    'Anne', 'Mary', 'Jack', 'Morgan', 'Roger',
    'Bill', 'Ragnar', 'Ed', 'John', 'Jane' ];
  final List _appellations = [
    'Jackal', 'King', 'Red', 'Stalwart', 'Axe',
    'Young', 'Brave', 'Eager', 'Wily', 'Zesty'];

Key information

Provide helper methods that retrieve a randomly chosen first name and appellation.

class NameService {
  ...

  String _randomFirstName()
      => _names[_indexGen.nextInt(_names.length)];

  String _randomAppellation() =>
      _appellations[_indexGen.nextInt(_appellations.length)];
}

Key information

Provide a method that gets a pirate name.

class NameService {
  ...
  String getPirateName(String firstName) {
    if (firstName == null || firstName.trim().isEmpty) {
      firstName = _randomFirstName();
    }

    return '$firstName the ${_randomAppellation()}';
  }
}

Key information

Edit badge_component.dart

Hook up the name service to the badge component.

Import the name service.

import 'package:angular2/core.dart';
import 'name_service.dart';

Add NameService as a provider by adding the text , providers: const [NameService] to the @Component annotation. After formatting, it should look as follows:

@Component(
    selector: 'pirate-badge',
    templateUrl: 'badge_component.html',
    styleUrls: const ['badge_component.css'],
    providers: const [NameService])
class BadgeComponent {
  ...
}

Key information

Add a _nameService instance variable.

class BadgeComponent {
  final NameService _nameService;
  ...
}

Key information

Add a constructor that assigns a value to _nameService.

class BadgeComponent {
  ...
  BadgeComponent(this._nameService);
  ...
}

Key information

BadgeComponent(var nameService) {
  _nameService = nameService;
}

But since _nameService is final, it has to be initialized when it's declared, or in the constructor's argument list.

Add a setBadgeName() method.

class BadgeComponent {
  ...
  void setBadgeName([String newName = '']) {
    if (newName == null) return;
    badgeName = _nameService.getPirateName(newName);
  }
}

Key information

Modify the generateBadge() and updateBadge() methods.

class BadgeComponent implements OnInit {
  ...
  void generateBadge() {
    setBadgeName();
  }

  void updateBadge(String inputName) {
    setBadgeName(inputName);
    ...
}

Test it!

In IntelliJ, double-click the web/index.html file to open the file in the editor view. Hover your mouse pointer over the code to show the browser icons bar, and click the Dartium icon on the far right.

Click the button—each click displays a new pirate name composed of a name and an appellation.

Problems?

Look in IntelliJ's window for possible errors. If that fails, look your browser's JavaScript console. In Dartium or Chrome, bring up the console using View > Developer > JavaScript Console.

Finally, if you still haven't found the problem check your code against the files in 5-piratenameservice.

In this final step, you learn about Dart's support for asynchronous file I/O as you modify the name service to fetch the names and appellations from a JSON file on www.dartlang.org.

Edit name_service.dart

Add imports to the top.

import 'dart:async';
import 'dart:convert';
import 'dart:html';
...

Key information

After the import, add a constant defining the location of the JSON file.

import ...

const _namesPath =
    'https://www.dartlang.org/codelabs/darrrt/files/piratenames.json';

Replace _names and _appellations with empty lists.

class NameService {
  ...

  final _names = <String>[];
  final _appellations = <String>[];
  ...
}

Key information

Add a function, readyThePirates(), to read the names and appellations from the JSON file.

class NameService {
  ...

  Future readyThePirates() async {
    if (_names.isNotEmpty && _appellations.isNotEmpty) return;

    var jsonString = await HttpRequest.getString(_namesPath);
    var pirateNames = JSON.decode(jsonString);
    _names.addAll(pirateNames['names']);
    _appellations.addAll(pirateNames['appellations']);
  }
  ...
}

Key information

Edit badge_component.html

Enable the input field depending on the value of a property.

<div class="widgets">
  <input [disabled]="!isInputEnabled" (input)="updateBadge($event.target.value)"
         type="text" maxlength="15">
  ...
</div>

Key information

Edit badge_component.dart

Load the pirate names and appellations from a JSON file. When successfully loaded, enable the UI.

At startup, disable the button and input field.

class BadgeComponent {
  ...
  bool isButtonEnabled = false;
  bool isInputEnabled = false;
}

After the constructor, add a function to get the names from the JSON file, handling both success and failure.

class BadgeComponent implements OnInit {
  ...

  ngOnInit() async {
    try {
      await _nameService.readyThePirates();
      //on success
      isButtonEnabled = true;
      isInputEnabled = true;
    } catch (arrr) {
      badgeName = 'Arrr! No names.';
      print('Error initializing pirate names: $arrr');
    }
  }
  ...
}

Key information

Test it!

In IntelliJ, double-click the web/index.html file to open the file in the editor view. Hover your mouse pointer over the code to show the browser icons bar, and click the Dartium icon on the far right.

The app should work as before, but this time the pirate name is constructed from the JSON file.

Problems?

Look in IntelliJ's window for possible errors. If that fails, look your browser's JavaScript console. In Dartium or Chrome, bring up the console using View > Developer > JavaScript Console.

Finally, if you still haven't found the problem check your code against the files in 6-readjsonfile.

You've written an Angular 2 for Dart web app!

Now that you've written your app, what do you do next? Here are some suggestions.

Work through the guides on angular.io

Work through the QuickStart and Developer Guides on angular.io.

Build your app to run in any browser

You can test your app in other browsers by right-clicking index.html and choosing Open in Browser from the pop up menu.

To compile the app into JavaScript that runs in any modern browser, use pub build. Build the app in IntelliJ, as follows:

  1. Open pubspec.yaml and click Build....
  2. From the dialog, choose Release or Debug, as desired. The third option allows you to specify a mode to pub build.

Read the language and library tours

Learn more about Dart from the language tour and library tour.