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.

# Child & Relative Routes (DRAFT) 6.0

If you notice any issues with this page, please report them.

<?code-excerpt path-base=”examples/ng/doc/router”?> <?code-excerpt replace=”/_\d((.template)?.(dart|html|css))/$1/g”?>

Milestone

In this milestone you’ll create a routing component, independent from the app root, that will manage routing for the crisis center. The app root will play the role of parent rooting component.

## Crisis center organization

You’ll organize the crisis center to conform to the recommended pattern for Angular apps:

- All crisis center files will be contained in a separate folder (`crisis`).
- The crisis center will have its own root component (`CrisisListComponent`).
- This root component will have its own router outlet and child routes.

If your app had many feature groups, the app component trees might look like this:

It’s time to add real functionality to the app’s current placeholder crisis list component. As a quick way to upgrade the basic functionality of the crisis list component:

- Delete the placeholder crisis component file.
- Create a `lib/src/crisis` folder.
- Copy the files from `lib/src/hero` into the new `crisis` folder, but rename files, and inside each file, change every mention of “hero” to “crisis”, and “heroes” to “crises”.
- Add the `CrisisService` to the `providers` list of `CrisisListComponent`.
- Define the following mock crises:

<?code-excerpt “lib/src/crisis/mock_crises.dart” title?>

      import 'crisis.dart';

      final List<Crisis> mockCrises = [
        Crisis(1, 'Dragon Burning Cities'),
        Crisis(2, 'Sky Rains Great White Sharks'),
        Crisis(3, 'Giant Asteroid Heading For Earth'),
        Crisis(4, 'Procrastinators Meeting Delayed Again')
      ];

open_in_browser Refresh the browser and ensure that the app’s hero features work as they did before, and that the crisis lists appears. You can’t select a crisis until you’ve added the appropriate crisis routes.

Service isolation note: `CrisisService` is declared in the `CrisisListComponent` providers list. This step limits the scope of the service to the app component subtree rooted at the crisis list component. No component outside of the crisis center needs access to the `CrisisService`.

## Routing component

The `CrisisListComponent` is a routing component like the `AppComponent`. It has its own router outlet and its own routes. In contrast to the hero list, the `CrisisListComponent` will display crisis details in a child view of the crisis list. View changes will be triggered by navigation.

Make the following changes to the crisis list component.

### Define routes

Create a route path and a route defintion for the child route to use to access crisis details:

<?code-excerpt “lib/src/crisis/route_paths.dart” plaster=”none” region=”v1” title?>

      import 'package:angular_router/angular_router.dart';

      import '../route_paths.dart' as _parent;

      export '../route_paths.dart' show idParam, getId;

      class RoutePaths {
        static final crisis = RoutePath(
          path: ':${_parent.idParam}',
          parent: _parent.RoutePaths.crises,
        );
      }

<?code-excerpt “lib/src/crisis/routes.dart” plaster=”none” region=”v1” title?>

      import 'package:angular_router/angular_router.dart';

      import 'crisis_component.template.dart' as crisis_template;
      import 'route_paths.dart';

      export 'route_paths.dart';

      class Routes {
        static final crisis = RouteDefinition(
          routePath: RoutePaths.crisis,
          component: crisis_template.CrisisComponentNgFactory,
        );

        static final all = <RouteDefinition>[
          crisis,
        ];
      }

### Add a router outlet

Make the following changes to the crisis list component:

- Drop the app route paths import.
- Import the crisis routes.
- Export `RoutePaths` and `Routes` using the `exports` argument of the `@Component` annotation.
- Add RouterOutlet to the crisis list component `directives` metadata.

<?code-excerpt “lib/src/crisis/crisis_list_component_4.dart (routes)” replace=”/import.|RouterOutlet|exports./[!$&!]/g” title?>

      [!import 'routes.dart';!]

      @Component(
        selector: 'my-crises',
        // ···
        directives: [coreDirectives, [!RouterOutlet!]],
        // ···
        [!exports: [RoutePaths, Routes],!]
      )
      class CrisisListComponent implements OnActivate {
        final CrisisService _crisisService;
        final Router _router;
        List<Crisis> crises;
        Crisis selected;

        CrisisListComponent(this._crisisService, this._router);
        // ···
      }

Add a `<router-outlet>` element to the end of the component template, binding the routes input to `Routes.all`:

<?code-excerpt “lib/src/crisis/crisis_list_component.html” replace=”/.router-outlet.*/[!$&!]/g” title?>

      <h2>Crisis Center</h2>
      <ul class="items">
        <li *ngFor="let crisis of crises"
          [class.selected]="crisis === selected"
          (click)="onSelect(crisis)">
          <span class="badge">{{crisis.id}}</span> {{crisis.name}}
        </li>
      </ul>
      [!<router-outlet [routes]="Routes.all"></router-outlet>!]

open_in_browser Refresh the browser. Select a crisis and its details appear! But notice that the selected crisis isn’t shown as active in the list.

### Show selected crisis as active

The crisis list selection code is still based on the hero list implementation: a crisis is selected when an ID is provided as an optional query parameter (try it: localhost:8080/#/crises?id=2):

<?code-excerpt “lib/src/crisis/crisis_list_component_3.dart (_select)” replace=”/queryParameters/[!$&!]/g” title?>

      Crisis _select(RouterState routerState) {
        final id = getId(routerState.[!queryParameters!]);
        return id == null
            ? null
            : crises.firstWhere((e) => e.id == id, orElse: () => null);
      }

This isn’t the behavior you want for the crisis center. Instead you want the following paths processing:

- `/crises` results in a crisis list without any selected item.
- `/crises/id` both:
  - Displays the details of the identified crisis
  - Shows this crisis as active in the list.

Crisis list highlighting can be fixed using the following simple change:

<?code-excerpt “lib/src/crisis/crisis_list_component.dart (_select)” replace=”/parameters/[!$&!]/g” title?>

      Crisis _selectHero(RouterState routerState) {
        final id = getId(routerState.[!parameters!]);
        return id == null
            ? null
            : crises.firstWhere((e) => e.id == id, orElse: () => null);
      }

open_in_browser Refresh the browser and select a crisis. The selected crisis is shown as active in the list and crisis details appear.

## Crisis center home

Through its router outlet, the crisis list component displays the details of a selected crisis. What should it display when no crisis is selected?

### Crisis home component

For this purpose, create the following “home” component:

<?code-excerpt “lib/src/crisis/crisis_list_home_component.dart” title?>

      import 'package:angular/angular.dart';

      @Component(
        selector: 'crises-home',
        template: '<p>Welcome to the Crisis Center</p>',
      )
      class CrisisListHomeComponent {}

### Crisis home route

Add a path and route to this component:

<?code-excerpt “lib/src/crisis/route_paths.dart (home)” replace=”/home/[!$&!]/g” title?>

      static final [!home!] = RoutePath(
        path: '',
        parent: _parent.RoutePaths.crises,
        useAsDefault: true,
      );

<?code-excerpt “lib/src/crisis/routes.dart (home)” replace=”/\b_home/[!$&!]/g” title?>

      import 'crisis_list_home_component.template.dart' as crisis_list_home_template;
      import 'route_paths.dart';

      export 'route_paths.dart';

      class Routes {
        // ···
        static final home = RouteDefinition(
          routePath: RoutePaths.home,
          component: crisis_list_home_template.CrisisListHomeComponentNgFactory,
          useAsDefault: true,
        );

        static final all = <RouteDefinition>[
          // ···
          home,
        ];
      }

Now two routes navigate to the crisis center child components, `CrisisListHomeComponent` and `CrisisComponent`, respectively.

Being child routes, there are some important differences in the way the router treats them:

- The router displays the components of these routes in the router outlet of the `CrisisListComponent`, not in the router outlet of the `AppComponent` shell.
- At the top level, paths that begin with `/` refer to the root of the app. But child route’s path extend the path of the parent route. With each step down the route tree, you add a slash followed by the route path, unless the path is empty.

Apply that logic to navigation within the crisis area for which the parent path is `/crises`.

- To navigate to the `CrisisListHomeComponent`, the full URL is `/crises` (`/crises` + `''`).

- To navigate to the `CrisisComponent` for a crisis with `id=2`, the full URL is `/crises/2` (`/crises` + `'/'` + `'2'`).

The absolute URL for the latter example, including the `localhost` origin, is localhost:8080/#/crises/2.

Looking at the `CrisisListComponent` alone, you can’t tell that it is a child routing component. You can’t tell that its routes are child routes, indistinguiable from top level app routes. This is intentional, and it makes it easier to reuse component routers. What makes a component a child router of our app is determined by the parent route configuration.

open_in_browser Refresh the browser and visit the crisis center to see the home component rendered. Select a crisis and its details appear. Click Cancel and the home welcome message reappears.

TODO: continue here. You should be able to select crises and see their details. Unfortunately, the crisis details Back button doesn’t work yet. You’ll fix that next.

### Navigating back to home

The currently faulty crisis view Back button has an event binding to this method:

<?code-excerpt “lib/src/crisis/crisis_component_3.dart (goBack)”?>

      Future<NavigationResult> goBack() => _router.navigate(
          RoutePaths.heroes.toUrl(),
          NavigationParams(queryParameters: {idParam: '${crisis.id}'}));

This is the version of the method copied from the hero component. It attempts to navigate back to the hero view, using paths defined for the app root.

To fix this, first adjust the route path import URI so that it refers to crisis route paths:

<?code-excerpt “lib/src/crisis/crisis_component_4.dart (import)” title?>

      import 'route_paths.dart';

Change the `navigate()` method argument to be the path to the crisis home:

<?code-excerpt “lib/src/crisis/crisis_component_4.dart (goBack)” title?>

      Future<NavigationResult> goBack() =>
          _router.navigate(RoutePaths.home.toUrl());

open_in_browser Refresh the browser, select a crisis and ensure that the Back button causes the crisis details view to be replaced by the crises home message.

## App code

After these changes, the folder structure looks like this:

- router_example
  - lib
    - app_component.dart
    - src
      - crisis
        - crises_component.{css,dart,html}
        - crises_home_component.dart
        - crisis.dart
        - crisis_component.{css,dart,html}
        - crisis_service.dart
        - mock_crises.dart
        - route_paths.dart
        - routes.dart
      - hero
        - hero.dart
        - hero_component.{css,dart,html}
        - hero_service.dart
        - hero_list_component.{css,dart,html}
        - mock_heroes.dart
      - route_paths.dart
      - routes.dart
  - web
    - index.html
    - main.dart
    - styles.css

Here are key files for this version of the sample app: