πŸ“±
M335 - Mobile-Applikation realisieren
  • ▢️Intro
  • πŸ—“οΈKursplanung & Organisation
  • πŸ› οΈInfrastruktur und Tools
  • ❓FAQ
  • TAG 1
    • πŸ“–Native, Hybrid, Web
    • πŸ“–Frameworks
    • πŸ“–Angular
    • πŸ¦Έβ€β™‚οΈTour of Heroes
      • πŸ“–Introduction
      • πŸ“–Create a project
      • πŸ“–1. The hero editor
        • πŸ’‘Hinweise und Zusatzaufgaben zu Kapitel 1
      • πŸ“–2. Display a list
        • πŸ’‘Hinweise und Zusatzaufgaben zu Kapitel 2
      • πŸ“–3. Create a feature component
        • πŸ’‘Hinweise zu Kapitel 3
      • πŸ“–4. Add services
        • πŸ’‘Hinweise zu Kapitel 4
      • πŸ“–5. Navigation
        • πŸ’‘Hinweise und Zusatzaufgaben zu Kapitel 5
        • πŸ’‘ErgΓ€nzend zu Navigation
      • πŸ’‘ Debugging
    • πŸ“–CSS / SASS / SCSS
  • Tag 2
  • πŸ“–Ionic
  • ❓Ionic Projekt erstellen
  • πŸ’‘Ionic Übungen
  • πŸ“–Usability
  • πŸ“–Prototyping
  • πŸ“–UI Elements
  • πŸ› οΈ Projektsetup
  • Tag 3
    • πŸ“–Native Grundlagen
    • πŸ“–Capacitor EinfΓΌhrung
    • πŸ’‘App auf GerΓ€t laufen lassen
    • πŸ’‘Native Komponente mit Capacitor verwenden
    • ❓Starten auf GerΓ€t mit Live-Reload
  • πŸ“–Persistenz
    • πŸ“–Online Persistenz
    • πŸ“–Offline Persistenz
  • Tag 4
    • πŸ“–Testing
      • ❓Testplan: Praxisbeispiel
      • ❓Testprotokoll: Praxisbeispiel
  • Tag 5
    • πŸ’‘Build, VerΓΆffentlichung, Ausblick
Powered by GitBook
On this page
  • Routes
  • Register the routes
  • Add RouterOutlet
  • Try it
  • Add a navigation link using routerLink
  • Add a dashboard view
  • Add the dashboard route
  • Add a default route
  • Add dashboard link to the shell
  • Navigating to hero details
  • Delete hero details from HeroesComponent
  • Add a hero detail route
  • DashboardComponent hero links
  • HeroesComponent hero links
  • Routable HeroDetailComponent
  • Extract the id route parameter
  • Add HeroService.getHero()
  • Find the way back
  • Final code review
  • AppComponent
  • DashboardComponent
  • HeroesComponent
  • HeroDetailComponent
  • Summary
  • β†’ Hier geht es weiter
  1. TAG 1
  2. Tour of Heroes

5. Navigation

PreviousHinweise zu Kapitel 4NextHinweise und Zusatzaufgaben zu Kapitel 5

Last updated 4 months ago

The Tour of Heroes application has new requirements:

  • Add a Dashboard view

  • Add the ability to navigate between the Heroes and Dashboard views

  • When users click a hero name in either view, navigate to a detail view of the selected hero

  • When users click a deep link in an email, open the detail view for a particular hero

For the sample application that this page describes, see the / .

When you're done, users can navigate the application like this:

Routes

In Angular the routes of an application are configured in the app.routes.ts file.

Routes tell the Router which view to display when a user clicks a link or pastes a URL into the browser address bar.

Since app.routes.ts already imports HeroesComponent, you can use it in the routes array:

export const routes: Routes = [
  {
    path: 'heroes',
    loadComponent: () =>
      import('./heroes/heroes.component').then((m) => m.HeroesComponent),
  },
];
PROPERTIES
DETAILS

path

A string that matches the URL in the browser address bar.

component or loadComponent

The component that the router should create when navigating to this route.

This tells the router to match that URL to path: 'heroes' and display the HeroesComponent when the URL is something like localhost:4200/heroes.

Register the routes

In the file app.config.ts these routes are imported with provideRouter(routes).

Open the AppComponent template and replace the <app-heroes> element with a <router-outlet> element.

<h1>{{title}}</h1>
<router-outlet></router-outlet>
<app-messages></app-messages>

The AppComponent template no longer needs <app-heroes> because the application only displays the HeroesComponent when the user navigates to it.

The <router-outlet> tells the router where to display routed views.

The RouterOutlet is one of the router directives that became available to the AppComponent because the Angular router is imported with provideRouter in the app.config.ts file.

Try it

If you're not still serving your application, run ng serve to see your application in the browser.

The browser should refresh and display the application title but not the list of heroes.

Look at the browser's address bar. The URL ends in /. The route path to HeroesComponent is /heroes.

Append /heroes to the URL in the browser address bar. You should see the familiar heroes overview/detail view.

Remove /heroes from the URL in the browser address bar. The browser should refresh and display the application title but not the list of heroes.

Add a navigation link using routerLink

Ideally, users should be able to click a link to navigate rather than pasting a route URL into the address bar.

Add a <nav> element and, within that, an anchor element that, when clicked, triggers navigation to the HeroesComponent. The revised AppComponent template looks like this:

<h1>{{title}}</h1>
<nav>
  <a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>

A routerLink attribute is set to "/heroes", the string that the router matches to the route to HeroesComponent. The routerLink is the selector for the RouterLink directive that turns user clicks into router navigations. It's another of the public directives in the RouterModule.

The browser refreshes and displays the application title and heroes link, but not the heroes list.

Click the link. The address bar updates to /heroes and the list of heroes appears.

Make this and future navigation links look better by adding private CSS styles to app.component.css as listed in the final code review below.

Add a dashboard view

Routing makes more sense when your application has more than one view, yet the Tour of Heroes application has only the heroes view.

To add a DashboardComponent, run ng generate as shown here:

ng generate component dashboard

ng generate creates the files for the DashboardComponent.

Replace the default content in these files as shown here:

<h2>Top Heroes</h2>
<div class="heroes-menu">
  @for(hero of heroes; track hero.id) {
    <a>
      {{hero.name}}
    </a>
  }
</div>
import { Component, OnInit, inject } from '@angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: [ './dashboard.component.css' ],
  standalone: true,
})
export class DashboardComponent implements OnInit {
  private heroService = inject(HeroService)

  heroes: Hero[] = [];

  ngOnInit(): void {
    this.getHeroes();
  }

  getHeroes(): void {
    this.heroService.getHeroes()
      .subscribe(heroes => this.heroes = heroes.slice(1, 5));
  }
}
/* DashboardComponent's private CSS styles */

h2 {
  text-align: center;
}

.heroes-menu {
  padding: 0;
  margin: auto;
  max-width: 1000px;

  /* flexbox */
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-around;
  align-content: flex-start;
  align-items: flex-start;
}

a {
  background-color: #3f525c;
  border-radius: 2px;
  padding: 1rem;
  font-size: 1.2rem;
  text-decoration: none;
  display: inline-block;
  color: #fff;
  text-align: center;
  width: 100%;
  min-width: 70px;
  margin: .5rem auto;
  box-sizing: border-box;

  /* flexbox */
  order: 0;
  flex: 0 1 auto;
  align-self: auto;
}

@media (min-width: 600px) {
  a {
    width: 18%;
    box-sizing: content-box;
  }
}

a:hover {
  background-color: #000;
}

The template presents a grid of hero name links.

  • The @for repeater creates as many links as are in the component's heroes array.

  • The links are styled as colored blocks by the dashboard.component.css.

  • The links don't go anywhere yet.

The class is like the HeroesComponent class.

  • It defines a heroes array property

  • The constructor expects Angular to inject the HeroService into a private heroService property

  • The ngOnInit() lifecycle hook calls getHeroes()

This getHeroes() returns the sliced list of heroes at positions 1 and 5, returning only Heroes two, three, four, and five.

getHeroes(): void {
  this.heroService.getHeroes()
    .subscribe(heroes => this.heroes = heroes.slice(1, 5));
}

Add the dashboard route

To navigate to the dashboard, the router needs an appropriate route.

Add a route to the routes array that matches a path to the DashboardComponent.

  {
    path: 'dashboard',
    loadComponent: () =>
      import('./dashboard/dashboard.component').then(
        (m) => m.DashboardComponent,
      ),
  },

Add a default route

When the application starts, the browser's address bar points to the web site's root. That doesn't match any existing route so the router doesn't navigate anywhere. The space below the <router-outlet> is blank.

To make the application navigate to the dashboard automatically, add the following route to the routes array.

{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },

This route redirects a URL that fully matches the empty path to the route whose path is '/dashboard'.

After the browser refreshes, the router loads the DashboardComponent and the browser address bar shows the /dashboard URL.

Add dashboard link to the shell

The user should be able to navigate between the DashboardComponent and the HeroesComponent by clicking links in the navigation area near the top of the page.

Add a dashboard navigation link to the AppComponent shell template, just above the Heroes link.

<h1>{{title}}</h1>
<nav>
  <a routerLink="/dashboard">Dashboard</a>
  <a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>

After the browser refreshes you can navigate freely between the two views by clicking the links.

Navigating to hero details

The HeroDetailComponent displays details of a selected hero. At the moment the HeroDetailComponent is only visible at the bottom of the HeroesComponent

The user should be able to get to these details in three ways.

  1. By clicking a hero in the dashboard.

  2. By clicking a hero in the heroes list.

  3. By pasting a "deep link" URL into the browser address bar that identifies the hero to display.

This section enables navigation to the HeroDetailComponent and liberates it from the HeroesComponent.

Delete hero details from HeroesComponent

When the user clicks a hero in HeroesComponent, the application should navigate to the HeroDetailComponent, replacing the heroes list view with the hero detail view. The heroes list view should no longer show hero details as it does now.

Open the heroes/heroes.component.html and delete the <app-hero-detail> element from the bottom.

Clicking a hero item now does nothing. You can fix that after you enable routing to the HeroDetailComponent.

Add a hero detail route

A URL like ~/detail/11 would be a good URL for navigating to the Hero Detail view of the hero whose id is 11.

Add a parameterized route to the routes array that matches the path pattern to the hero detail view.

  {
    path: 'detail/:id',
    loadComponent: () =>
      import('./hero-detail/hero-detail.component').then(
        (m) => m.HeroDetailComponent,
      ),
  },

The colon : character in the path indicates that :id is a placeholder for a specific hero id.

At this point, all application routes are in place.

export const routes: Routes = [
  {
    path: '',
    redirectTo: '/dashboard',
    pathMatch: 'full',
  },
  {
    path: 'dashboard',
    loadComponent: () =>
      import('./dashboard/dashboard.component').then(
        (m) => m.DashboardComponent,
      ),
  },
  {
    path: 'heroes',
    loadComponent: () =>
      import('./heroes/heroes.component').then((m) => m.HeroesComponent),
  },
  {
    path: 'detail/:id',
    loadComponent: () =>
      import('./hero-detail/hero-detail.component').then(
        (m) => m.HeroDetailComponent,
      ),
  },
];

DashboardComponent hero links

The DashboardComponent hero links do nothing at the moment.

Now that the router has a route to HeroDetailComponent, fix the dashboard hero links to navigate using the parameterized dashboard route.

  @for (hero of heroes; track hero.id) {
    <a routerLink="/detail/{{ hero.id }}">
      {{ hero.name }}
    </a>
  }

You're using Angular interpolation binding within the @for repeater to insert the current iteration's hero.id into each routerLink.

HeroesComponent hero links

The hero items in the HeroesComponent are <li> elements whose click events are bound to the component's onSelect() method.

<ul class="heroes">
  @for (hero of heroes; track hero.id) {
    <li>
      <button type="button" (click)="onSelect(hero)" [class.selected]="hero === selectedHero">
        <span class="badge">{{hero.id}}</span>
        <span class="name">{{hero.name}}</span>
      </button>
    </li>
  }
</ul>
<ul class="heroes">
  @for (hero of heroes; track hero.id) {
    <li>
      <a routerLink="/detail/{{hero.id}}">
        <span class="badge">{{hero.id}}</span> {{hero.name}}
      </a>
    </li>
  }
</ul>

Be sure to fix the private style sheet in heroes.component.css to make the list look as it did before. Revised styles are in the final code review at the bottom of this guide.

Remove dead code - optional

While the HeroesComponent class still works, the onSelect() method and selectedHero property are no longer used.

It's nice to tidy things up for your future self. Here's the class after pruning away the dead code.

Routable HeroDetailComponent

The parent HeroesComponent used to set the HeroDetailComponent.hero property and the HeroDetailComponent displayed the hero.

HeroesComponent doesn't do that anymore. Now the router creates the HeroDetailComponent in response to a URL such as ~/detail/12.

The HeroDetailComponent needs a new way to get the hero to display. This section explains the following:

  • Get the route that created it

  • Extract the id from the route

  • Get the hero with that id from the server using the HeroService

Add the following imports:

import { Component, inject, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { HeroService } from '../hero.service';

If you are using WebStorm as your IDE, it will usually help you and automatically create the imports, as soon as you use those elements. Ask your instructor for a demo.

Inject the ActivatedRoute, HeroService, and Location services, saving their values in private fields:

private route = inject(ActivatedRoute);
private heroService = inject(HeroService);
private location = inject(Location);

The ActivatedRoute holds information about the route to this instance of the HeroDetailComponent. This component is interested in the route's parameters extracted from the URL. The "id" parameter is the id of the hero to display.

The HeroService gets hero data from the remote server and this component uses it to get the hero-to-display.

The location is an Angular service for interacting with the browser. This service lets you navigate back to the previous view.

Extract the id route parameter

In the ngOnInit() lifecycle hook call getHero() and define it as follows.

ngOnInit(): void {
  this.getHero();
}

getHero(): void {
  const id = Number(this.route.snapshot.paramMap.get('id'));
  this.heroService.getHero(id)
    .subscribe(hero => this.hero = hero);
}

The route.snapshot is a static image of the route information shortly after the component was created.

The paramMap is a dictionary of route parameter values extracted from the URL. The "id" key returns the id of the hero to fetch.

Route parameters are always strings. The JavaScript Number function converts the string to a number, which is what a hero id should be.

The browser refreshes and the application crashes with a compiler error. HeroService doesn't have a getHero() method. Add it now.

Add HeroService.getHero()

Open HeroService and add the following getHero() method with the id after the getHeroes() method:

getHero(id: number): Observable<Hero> {
  // For now, assume that a hero with the specified `id` always exists.
  // Error handling will be added in the next step of the tutorial.
  const hero = HEROES.find(h => h.id === id)!;
  this.messageService.add(`HeroService: fetched hero id=${id}`);
  return of(hero);
}

Like getHeroes(), getHero() has an asynchronous signature. It returns a mock hero as an Observable, using the RxJS of() function.

You can rewrite getHero() as a real Http request without having to change the HeroDetailComponent that calls it.

Try it

The browser refreshes and the application is working again. You can click a hero in the dashboard or in the heroes list and navigate to that hero's detail view.

If you paste localhost:4200/detail/12 in the browser address bar, the router navigates to the detail view for the hero with id: 12, Dr Nice.

Find the way back

By clicking the browser's back button, you can go back to the previous page. This could be the hero list or dashboard view, depending upon which sent you to the detail view.

It would be nice to have a button on the HeroDetail view that can do that.

Add a go back button to the bottom of the component template and bind it to the component's goBack() method.

<button type="button" (click)="goBack()">go back</button>

Add a goBack() method to the component class that navigates backward one step in the browser's history stack using the Location service that you used to inject.

goBack(): void {
  this.location.back();
}

Refresh the browser and start clicking. Users can now navigate around the application using the new buttons.

The details look better when you add the private CSS styles to hero-detail.component.css as listed in one of the "final code review" tabs below.

Final code review

Here are the code files discussed on this page.

Routing and HeroService

import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: '',
    redirectTo: '/dashboard',
    pathMatch: 'full',
  },
  {
    path: 'dashboard',
    loadComponent: () =>
      import('./dashboard/dashboard.component').then(
        (m) => m.DashboardComponent,
      ),
  },
  {
    path: 'heroes',
    loadComponent: () =>
      import('./heroes/heroes.component').then((m) => m.HeroesComponent),
  },
  {
    path: 'detail/:id',
    loadComponent: () =>
      import('./hero-detail/hero-detail.component').then(
        (m) => m.HeroDetailComponent,
      ),
  }
];
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Hero } from '../types/hero';
import { MessageService } from './message.service';
import { HEROES } from '../data/mock-heroes';

@Injectable({
  providedIn: 'root',
})
export class HeroService {
  constructor(private messageService: MessageService) {}

  getHeroes(): Observable<Hero[]> {
    const heroes = of(HEROES);
    this.messageService.add('HeroService: fetched heroes');
    console.log('messages: ', this.messageService.messages);

    return heroes;
  }

  getHero(id: number): Observable<Hero> {
    const hero = HEROES.find((h) => h.id === id)!;
    this.messageService.add(`HeroService: fetched hero id=${id}`);
    return of(hero);
  }
}

AppComponent

<h1>{{title}}</h1>
<nav>
  <a routerLink="/dashboard">Dashboard</a>
  <a routerLink="/heroes">Heroes</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>
import { Component } from '@angular/core';
import { MessagesComponent } from './messages/messages.component';
import { RouterLink, RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  standalone: true,
  imports: [RouterLink, RouterOutlet, MessagesComponent],
})
export class AppComponent {
  title = 'Tour of Heroes';
}
/* AppComponent's private CSS styles */
h1 {
  margin-bottom: 0;
}
nav a {
  padding: 1rem;
  text-decoration: none;
  margin-top: 10px;
  display: inline-block;
  background-color: #e8e8e8;
  color: #3d3d3d;
  border-radius: 4px;
}

nav a:hover {
  color: white;
  background-color: #42545C;
}
nav a:active {
  background-color: black;
}

DashboardComponent

<h2>Top Heroes</h2>
<div class="heroes-menu">
  @for (hero of heroes; track hero.id) {
    <a routerLink="/detail/{{ hero.id }}">
      {{ hero.name }}
    </a>
  }
</div>
import { Component, inject, OnInit } from '@angular/core';
import { HeroService } from '../service/hero.service';
import { Hero } from '../types/hero';
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss'],
  standalone: true,
  imports: [RouterLink],
})
export class DashboardComponent implements OnInit {
  private heroService = inject(HeroService);
  heroes: Hero[] = [];

  ngOnInit(): void {
    this.getHeroes();
  }

  getHeroes(): void {
    this.heroService
      .getHeroes()
      .subscribe((heroes) => (this.heroes = heroes.slice(1, 5)));
  }
}
/* DashboardComponent's private CSS styles */

h2 {
  text-align: center;
}

.heroes-menu {
  padding: 0;
  margin: auto;
  max-width: 1000px;

  /* flexbox */
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-around;
  align-content: flex-start;
  align-items: flex-start;
}

a {
  background-color: #3f525c;
  border-radius: 2px;
  padding: 1rem;
  font-size: 1.2rem;
  text-decoration: none;
  display: inline-block;
  color: #fff;
  text-align: center;
  width: 100%;
  min-width: 70px;
  margin: .5rem auto;
  box-sizing: border-box;

  /* flexbox */
  order: 0;
  flex: 0 1 auto;
  align-self: auto;
}

@media (min-width: 600px) {
  a {
    width: 18%;
    box-sizing: content-box;
  }
}

a:hover {
  background-color: #000;
}

HeroesComponent

<h2>My Heroes</h2>
<ul class="heroes">
  @for (hero of heroes; track hero.id; let i = $index) {
    <li>
      <a routerLink="/detail/{{hero.id}}">
        <span class="badge">{{hero.id}}</span> {{hero.name}}
      </a>
    </li>
  }
</ul>
import { Component, inject, OnInit } from '@angular/core';
import { Hero } from '../types/hero';
import { HeroService } from '../service/hero.service';
import { BadgeComponent } from '../badge/badge.component';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.scss'],
  standalone: true,
  imports: [BadgeComponent],
})
export class HeroesComponent implements OnInit {
  private heroService = inject(HeroService);
  heroes: Hero[] = [];

  ngOnInit(): void {
    this.getHeroes();
  }

  getHeroes(): void {
    this.heroService.getHeroes().subscribe((heroes) => (this.heroes = heroes));
  }
}
/* HeroesComponent's private CSS styles */
.heroes {
  margin: 0 0 2em 0;
  list-style-type: none;
  padding: 0;
  width: 15em;
}
.heroes li {
  position: relative;
  cursor: pointer;
}

.heroes li:hover {
  left: .1em;
}

.heroes a {
  color: #333;
  text-decoration: none;
  background-color: #EEE;
  margin: .5em;
  padding: .3em 0;
  height: 1.6em;
  border-radius: 4px;
  display: block;
  width: 100%;
}

.heroes a:hover {
  color: #2c3a41;
  background-color: #e6e6e6;
}

.heroes a:active {
  background-color: #525252;
  color: #fafafa;
}

.heroes .badge {
  display: inline-block;
  font-size: small;
  color: white;
  padding: 0.8em 0.7em 0 0.7em;
  background-color: #405061;
  line-height: 1em;
  position: relative;
  left: -1px;
  top: -4px;
  height: 1.8em;
  min-width: 16px;
  text-align: right;
  margin-right: .8em;
  border-radius: 4px 0 0 4px;
}

HeroDetailComponent

@if (hero) {
  <div>
    <h2>{{ hero.name | uppercase }} Details</h2>
    <div><span>id: </span>{{ hero.id }}</div>
    <div class="hero-name-field">
      <label for="hero-name">Hero name: </label>
      <input id="hero-name" [(ngModel)]="hero.name" placeholder="Hero name" />
    </div>
    <div>
      <label for="hero-power">Hero power: </label>
      <input id="hero-power" [(ngModel)]="hero.power" placeholder="Hero power" />
    </div>
    <button type="button" (click)="goBack()">go back</button>
  </div>
}
import { Component, inject, OnInit } from '@angular/core';
import { Hero } from '../types/hero';
import { ActivatedRoute } from '@angular/router';
import { HeroService } from '../service/hero.service';
import { Location, UpperCasePipe } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.scss'],
  standalone: true,
  imports: [FormsModule, UpperCasePipe],
})
export class HeroDetailComponent implements OnInit {
  private route = inject(ActivatedRoute);
  private heroService = inject(HeroService);
  private location = inject(Location);

  hero: Hero | undefined;

  ngOnInit(): void {
    this.getHero();
  }

  getHero(): void {
    const id = Number(this.route.snapshot.paramMap.get('id'));
    this.heroService.getHero(id).subscribe((hero) => (this.hero = hero));
  }

  goBack(): void {
    this.location.back();
  }
}
/* HeroDetailComponent's private CSS styles */
label {
  color: #435960;
  font-weight: bold;
}
input {
  font-size: 1em;
  padding: .5rem;
}
button {
  margin-top: 20px;
  background-color: #eee;
  padding: 1rem;
  border-radius: 4px;
  font-size: 1rem;
}
button:hover {
  background-color: #cfd8dc;
}
button:disabled {
  background-color: #eee;
  color: #ccc;
  cursor: auto;
}

Summary

  • You added the Angular router to navigate among different components

  • You turned the AppComponent into a navigation shell with <a> links and a <router-outlet>

  • You configured the router in an app.routes.ts

  • You defined routes, a redirect route, and a parameterized route

  • You used the routerLink directive in anchor elements

  • You refactored a tightly coupled main/detail view into a routed detail view

  • You used router link parameters to navigate to the detail view of a user-selected hero

  • You shared the HeroService with other components

β†’ Hier geht es weiter

A typical Angular has two properties:

Add

Remove the inner HTML of <li>. Wrap the badge and name in an anchor <a> element. Add a attribute to the anchor that's the same as in the dashboard template.

IMPORTANT: The backtick ( ` ) characters define a JavaScript for embedding the id.

πŸ¦Έβ€β™‚οΈ
πŸ“–
Route
RouterOutlet
routerLink
template literal
πŸ’‘Hinweise und Zusatzaufgaben zu Kapitel 5
live example
download example