# Table of Contents - [Infrastruktur und Tools | M335 - Mobile-Applikation realisieren](#infrastruktur-und-tools-m335-mobile-applikation-realisieren) - [Intro | M335 - Mobile-Applikation realisieren](#intro-m335-mobile-applikation-realisieren) - [FAQ | M335 - Mobile-Applikation realisieren](#faq-m335-mobile-applikation-realisieren) - [Native, Hybrid, Web | M335 - Mobile-Applikation realisieren](#native-hybrid-web-m335-mobile-applikation-realisieren) - [Angular | M335 - Mobile-Applikation realisieren](#angular-m335-mobile-applikation-realisieren) - [Hinweise und Zusatzaufgaben zu Kapitel 1 | M335 - Mobile-Applikation realisieren](#hinweise-und-zusatzaufgaben-zu-kapitel-1-m335-mobile-applikation-realisieren) - [Frameworks | M335 - Mobile-Applikation realisieren](#frameworks-m335-mobile-applikation-realisieren) - [Hinweise zu Kapitel 4 | M335 - Mobile-Applikation realisieren](#hinweise-zu-kapitel-4-m335-mobile-applikation-realisieren) - [Hinweise zu Kapitel 3 | M335 - Mobile-Applikation realisieren](#hinweise-zu-kapitel-3-m335-mobile-applikation-realisieren) - [Kursplanung & Organisation | M335 - Mobile-Applikation realisieren](#kursplanung-organisation-m335-mobile-applikation-realisieren) - [Tour of Heroes | M335 - Mobile-Applikation realisieren](#tour-of-heroes-m335-mobile-applikation-realisieren) - [Introduction | M335 - Mobile-Applikation realisieren](#introduction-m335-mobile-applikation-realisieren) - [1. The hero editor | M335 - Mobile-Applikation realisieren](#1-the-hero-editor-m335-mobile-applikation-realisieren) - [3. Create a feature component | M335 - Mobile-Applikation realisieren](#3-create-a-feature-component-m335-mobile-applikation-realisieren) - [Hinweise und Zusatzaufgaben zu Kapitel 2 | M335 - Mobile-Applikation realisieren](#hinweise-und-zusatzaufgaben-zu-kapitel-2-m335-mobile-applikation-realisieren) - [Create a project | M335 - Mobile-Applikation realisieren](#create-a-project-m335-mobile-applikation-realisieren) - [2. Display a list | M335 - Mobile-Applikation realisieren](#2-display-a-list-m335-mobile-applikation-realisieren) - [4. Add services | M335 - Mobile-Applikation realisieren](#4-add-services-m335-mobile-applikation-realisieren) - [Ergänzend zu Navigation | M335 - Mobile-Applikation realisieren](#erg-nzend-zu-navigation-m335-mobile-applikation-realisieren) - [Hinweise und Zusatzaufgaben zu Kapitel 5 | M335 - Mobile-Applikation realisieren](#hinweise-und-zusatzaufgaben-zu-kapitel-5-m335-mobile-applikation-realisieren) --- # Infrastruktur und Tools | M335 - Mobile-Applikation realisieren [PreviousKursplanung & Organisation](/kursplanung-and-organisation) [NextFAQ](/faq) Last updated 10 months ago [](#kursunterlagen) Kursunterlagen --------------------------------------- Dieses Gitbook findet ihr unter . [](#moodle) Moodle ----------------------- Zusätzlich gibt es Unterlagen auf Moodle unter: . [](#tools) Tools --------------------- * Jetbrains Applikationen * Webstorm * Android Studio * auch Xcode möglich für iPhone mit Mac * Git * Gitbash * NodeJS In der Konsole oder im Terminal (ebenso verfügbar im Webstorm) folgenden Befehl ausführen. Dadurch werden die Angular und Ionic Commandline Interfaces installiert. Copy npm i -g @angular/cli npm i -g @ionic/cli # Auf macOS / Linux muss evt. sudo verwendet werden * Browser (Chrome, Firefox, Brave, ...) Jetbrains IDEs sind kostenpflichtig, jedoch stehen dir von deiner Berufsschule Lizenzen zur Verfügung. Dadurch bist du in der Lage diese Tools kostenlos zu nutzen. 🛠️ [m335.ict-bz.ch](https://m335.ict-bz.ch) [https://kurse.ict-bz.ch](https://kurse.ict-bz.ch/) --- # Intro | M335 - Mobile-Applikation realisieren [NextKursplanung & Organisation](/kursplanung-and-organisation) Last updated 3 months ago [](#uber-dieses-skript) Über dieses Skript ----------------------------------------------- Dieses Skript wurde im Rahmen des Modul 335 - "Mobile Applikation realisieren" für die von der NiceApps GmbH erstellt. Während den folgenden 5 Tagen soll in der mobilen Programmierung, mit dem hybriden UI-Framework Ionic und der Laufzeitumgebung Capacitor, ein tiefes Verständnis geschaffen werden. Dabei wird der Aufbau einer App, so wie auch die technische Umsetzung mit Angular angeschaut. #### [](#symbole) Symbole Symbol Bedeutung / Einsatz 🗓 Plan 🛠 Dieses Symbol taucht da auf, wo ihr selbst Hand anlegen könnt. Es wird gewerkelt. ❓ Da, wo Fragen beantwortet werden oder Schritt für Schritt-Anleitungen stehen, findet ihr dieses Symbol. 📖 Hier findet ihr Theorie und Wissen. 💡 Ihr seid gefragt! Hier wird geknobelt und probiert, denn Ihr löst Aufgaben oder programmiert euer Projekt. 💬 In der Klasse oder in kleineren Gruppen diskutiert ihr ein Thema. 🚩 Dies ist ein Meilenstein. [](#uber-jason) Über Jason ------------------------------- Hallo zusammen! Ich bin Jason, euer freundliches Maskottchen und Reiseleiter in der aufregenden Welt der mobilen Programmierung! Genau wie euer Instruktor bin auch ich ein Fan von sauberem Code, innovativen Apps und allem, was mit Technologie zu tun hat. Warum bin ich hier? Um euch die komplexen Konzepte der mobilen App-Entwicklung zu vereinfachen und ein wenig Spass in den Lernprozess zu bringen! Gemeinsam tauchen wir in die Geheimnisse von iOS, Android und Cross-Platform-Lösungen wie Capacitor ein. Ich werde euch nicht nur durch die Theorie führen, sondern auch praktische Aufgaben und Rätsel bereitstellen, damit ihr euer neu erworbenes Wissen gleich in die Praxis umsetzen könnt. Ausserdem stehe ich euch immer zur Seite, wenn ihr Fragen habt oder in einem kniffligen Code-Snippet feststeckt. Bereit für das Abenteuer? Schnallt euch an, ladet eure IDEs und lasst uns loslegen! 🚀 Herzliche Grüsse, Jason ▶️ [ICT-Berufsbildung Zentralschweiz](https://www.ict-bz.ch/) ![](https://m335.ict-bz.ch/~gitbook/image?url=https%3A%2F%2F1951332971-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FfaRLpqjavHkOOr8FPTA1%252Fuploads%252FkimaRpg4gMtIhRh6gb5E%252Fimage.png%3Falt%3Dmedia%26token%3Dc28d95df-5c31-48d3-b5b8-d7a640ec602e&width=768&dpr=4&quality=100&sign=a55d4514&sv=2) --- # FAQ | M335 - Mobile-Applikation realisieren [](#runtime-error-cannot-read-property-xyz-of-undefined) Runtime Error - Cannot read property ‘xyz’ of undefined --------------------------------------------------------------------------------------------------------------------- Ein klassischer Fehler welcher oft mit Objekten auftritt, aber nicht immer einfach zu finden ist. Dein Code sieht etwa so aus: Copy // Inside your class item: string; // Inside your view {{ item.myValue }} Das Property selber ist nicht das Problem, jedoch das Objekt selber. Es wird versucht ein Property `myValue` des nicht vorhanden oder null Objekts `item` aufzurufen. Prüfe die initialiserung des Objekts oder verwende den "safe navigation operator": Copy // ? operator prevents crashing here if item is null or undefined! {{ item?.myValue }} [](#runtime-error-uncaught-in-promise-invalid-link-mypage) Runtime Error - Uncaught (in promise): invalid link: MyPage --------------------------------------------------------------------------------------------------------------------------- Dieser Fehler tritt auf wenn du versuchst auf eine Seite MyPage zu navigieren, die nicht existiert. Hast du den Namen richtig geschrieben? [](#runtime-error-_co.myfunction-is-not-a-function) Runtime Error - \_co.myFunction is not a function ---------------------------------------------------------------------------------------------------------- Dieser Fehler ist relativ einfach zu finden. Du rufst in deinem Template eine nicht definierte Funktion myFunction auf (z.B. beim Klick eines Buttons). Stelle sicher das in deiner Component die entsprechende Funktion auch definiert ist. [](#mein-code-wird-nicht-angezeigt) Mein Code wird nicht angezeigt ----------------------------------------------------------------------- Wird z.B. dein neuer Code nicht angezeigt: Copy } This is an example of Angular's event binding syntax. The parentheses around `click` tell Angular to listen for the ` } [](#final-code-review) Final code review --------------------------------------------- Here are the code files discussed on this page, including the `HeroesComponent` styles. src/app/mock-heroes.tssrc/app/heroes/heroes.component.tssrc/app/heroes/heroes.component.htmlsrc/app/heroes/heroes.component.css Copy import { Hero } from './hero'; export const HEROES: Hero[] = [\ { id: 12, name: 'Dr. Nice', power: 'Healing' },\ { id: 13, name: 'Bombasto', power: 'Explosions' },\ { id: 14, name: 'Celeritas' },\ { id: 15, name: 'Magneta', power: 'Magnetism' },\ { id: 16, name: 'RubberMan', power: 'Elasticity' },\ { id: 17, name: 'Dynama' },\ { id: 18, name: 'Dr. IQ', power: 'Genius Intelligence' },\ { id: 19, name: 'Magma', power: 'Lava Manipulation' },\ { id: 20, name: 'Tornado', power: 'Wind Control' },\ ]; Copy import { Component } from '@angular/core'; import { UpperCasePipe } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { Hero } from '../hero'; import { HEROES } from '../mock-heroes'; @Component({ standalone: true, selector: 'app-heroes', templateUrl: './heroes.component.html', styleUrls: ['./heroes.component.css'], imports: [\ FormsModule,\ UpperCasePipe,\ ], }) export class HeroesComponent { heroes = HEROES; selectedHero?: Hero; onSelect(hero: Hero): void { this.selectedHero = hero; } } Copy

My Heroes

@if(selectedHero) {

{{selectedHero.name | uppercase}} Details

id: {{selectedHero.id}}
} Copy /* HeroesComponent's private CSS styles */ .heroes { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .heroes li { display: flex; } .heroes button { flex: 1; cursor: pointer; position: relative; left: 0; background-color: #EEE; margin: .5em; padding: 0; border-radius: 4px; display: flex; align-items: stretch; height: 1.8em; } .heroes button:hover { color: #2c3a41; background-color: #e6e6e6; left: .1em; } .heroes button:active { background-color: #525252; color: #fafafa; } .heroes button.selected { background-color: black; color: white; } .heroes button.selected:hover { background-color: #505050; color: white; } .heroes button.selected:active { background-color: black; color: white; } .heroes .badge { display: inline-block; font-size: small; color: white; padding: 0.8em 0.7em 0 0.7em; background-color: #405061; line-height: 1em; margin-right: .8em; border-radius: 4px 0 0 4px; } .heroes .name { align-self: center; } [](#summary) Summary ------------------------- * The Tour of Heroes application displays a list of heroes with a detail view. * The user can select a hero and see that hero's details. * You used `@for` to display a list. * You used `@if` to conditionally include or exclude a block of HTML. * You can toggle a CSS style class with a `class` binding. [](#greater-than-hier-geht-es-weiter) \-> Hier geht es weiter ------------------------------------------------------------------ For more details on accessibility, see . 🦸‍♂️ 📖 [live example](https://angular.io/generated/live-examples/toh-pt2/stackblitz.html) [download example](https://angular.io/generated/zips/toh-pt2/toh-pt2.zip) [Accessibility in Angular](https://angular.dev/best-practices/a11y) [💡Hinweise und Zusatzaufgaben zu Kapitel 2](/tag-1/tour-of-heroes/2.-display-a-list/hinweise-und-zusatzaufgaben-zu-kapitel-2) ![](https://m335.ict-bz.ch/~gitbook/image?url=https%3A%2F%2Fangular.io%2Fgenerated%2Fimages%2Fguide%2Ftoh%2Fheroes-list-selected.png&width=300&dpr=4&quality=100&sign=42db585&sv=2) --- # 4. Add services | M335 - Mobile-Applikation realisieren [PreviousHinweise zu Kapitel 3](/tag-1/tour-of-heroes/3.-create-a-feature-component/hinweise-zu-kapitel-3) [NextHinweise zu Kapitel 4](/tag-1/tour-of-heroes/4.-add-services/hinweise-zu-kapitel-4) Last updated 2 months ago The Tour of Heroes `HeroesComponent` is getting and displaying fake data. Refactoring the `HeroesComponent` focuses on supporting the view and making it easier to unit-test with a mock service. [](#why-services) Why services ----------------------------------- Components shouldn't fetch or save data directly, and they certainly shouldn't knowingly present fake data. They should focus on presenting data and delegate data access to a service. This tutorial creates a `HeroService` that all application classes can use to get heroes. Instead of creating that service with the , use the that Angular supports to inject it into the `HeroesComponent` constructor. Services are a great way to share information among classes that _don't know each other_. Create a `HeroService` next and inject it in the `HeroesComponent`, to provide hero data. [](#create-the-heroservice) Create the `HeroService` --------------------------------------------------------- Run `ng generate` to create a service called `hero`. Copy ng generate service hero The command generates a skeleton `HeroService` class in `src/app/hero.service.ts` as follows: src/app/hero.service.ts (new service) Copy import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class HeroService { constructor() { } } ### [](#get-hero-data) Get hero data The `HeroService` could get hero data from anywhere such as a web service, local storage, or a mock data source. Removing data access from components means you can change your mind about the implementation anytime, without touching any components. They don't know how the service works. The implementation in _this_ tutorial continues to deliver _mock heroes_. Import the `Hero` and `HEROES`. src/app/hero.service.ts Copy import { Hero } from './hero'; import { HEROES } from './mock-heroes'; Add a `getHeroes` method to return the _mock heroes_. src/app/hero.service.ts Copy getHeroes(): Hero[] { return HEROES; } [](#provide-the-heroservice) Provide the `HeroService` ----------------------------------------------------------- You must make the `HeroService` available to the dependency injection system before Angular can _inject_ it into the `HeroesComponent` by registering a _provider_. A provider is something that can create or deliver a service. In this case, it instantiates the `HeroService` class to provide the service. To make sure that the `HeroService` can provide this service, register it with the _injector_. The _injector_ is the object that chooses and injects the provider where the application requires it. Copy @Injectable({ providedIn: 'root', }) The `HeroService` is now ready to plug into the `HeroesComponent`. [](#update-heroescomponent) Update `HeroesComponent` --------------------------------------------------------- Open the `HeroesComponent` class file. Delete the `HEROES` import, because you won't need that anymore. Import the `HeroService` instead. src/app/heroes/heroes.component.ts (import HeroService) Copy import { HeroService } from '../hero.service'; Replace the definition of the `heroes` property with a declaration. src/app/heroes/heroes.component.ts Copy heroes: Hero[] = []; ### [](#inject-the-heroservice) Inject the `HeroService` Add a private `heroService` property of type `HeroService` to the component. src/app/heroes/heroes.component.ts Copy private heroService = inject(HeroService); The parameter simultaneously defines a private `heroService` property and identifies it as a `HeroService` injection site. ### [](#add-getheroes) Add `getHeroes()` Create a method to retrieve the heroes from the service. src/app/heroes/heroes.component.ts Copy getHeroes(): void { this.heroes = this.heroService.getHeroes(); } ### [](#call-it-in-ngoninit) Call it in `ngOnInit()` While you could call `getHeroes()` in the constructor, that's not the best practice. Reserve the constructor for minimal initialization such as wiring constructor parameters to properties. The constructor shouldn't _do anything_. It certainly shouldn't call a function that makes HTTP requests to a remote server as a _real_ data service would. src/app/heroes/heroes.component.ts Copy ngOnInit(): void { this.getHeroes(); } Important! The class should implement the matching Interface: Copy export class HeroesComponent implements OnInit {...} ### [](#see-it-run) See it run After the browser refreshes, the application should run as before, showing a list of heroes and a hero detail view when you click a hero name. [](#observable-data) Observable data ----------------------------------------- The `HeroService.getHeroes()` method has a _synchronous signature_, which implies that the `HeroService` can fetch heroes synchronously. The `HeroesComponent` consumes the `getHeroes()` result as if heroes could be fetched synchronously. src/app/heroes/heroes.component.ts Copy this.heroes = this.heroService.getHeroes(); This approach won't work in a real application that uses asynchronous calls. It works now because your service synchronously returns _mock heroes_. If `getHeroes()` can't return immediately with hero data, it shouldn't be synchronous, because that would block the browser as it waits to return data. `HeroService.getHeroes()` must have an _asynchronous signature_ of some kind. In this tutorial, `HeroService.getHeroes()` returns an `Observable` so that it can use the Angular `HttpClient.get` method to fetch the heroes and have `HttpClient.get()` return an `Observable`. ### [](#observable-heroservice) Observable `HeroService` In the tutorial on HTTP, you can see how Angular's `HttpClient` methods return RxJS `Observable` objects. This tutorial simulates getting data from the server with the RxJS `of()` function. Open the `HeroService` file and import the `Observable` and `of` symbols from RxJS. src/app/hero.service.ts (Observable imports) Copy import { Observable, of } from 'rxjs'; Replace the `getHeroes()` method with the following: src/app/hero.service.ts Copy getHeroes(): Observable { const heroes = of(HEROES); return heroes; } `of(HEROES)` returns an `Observable` that emits _a single value_, the array of mock heroes. ### [](#subscribe-in-heroescomponent) Subscribe in `HeroesComponent` The `HeroService.getHeroes` method used to return a `Hero[]`. Now it returns an `Observable`. You need to adjust your application to work with that change to `HeroesComponent`. Find the `getHeroes` method and replace it with the following code. the new code is shown side-by-side with the current version for comparison. heroes.component.ts (Observable)heroes.component.ts (Original) Copy getHeroes(): void { this.heroService.getHeroes() .subscribe(heroes => this.heroes = heroes); } Copy getHeroes(): void { this.heroes = this.heroService.getHeroes(); } `Observable.subscribe()` is the critical difference. The previous version assigns an array of heroes to the component's `heroes` property. The assignment occurs _synchronously_, as if the server could return heroes instantly or the browser could freeze the UI while it waited for the server's response. That _won't work_ when the `HeroService` is actually making requests of a remote server. The new version waits for the `Observable` to emit the array of heroes, which could happen now or several minutes from now. The `subscribe()` method passes the emitted array to the callback, which sets the component's `heroes` property. This asynchronous approach _works_ when the `HeroService` requests heroes from the server. [](#show-messages) Show messages ------------------------------------- This section guides you through the following: * Adding a `MessagesComponent` that displays application messages at the bottom of the screen * Creating an injectable, application-wide `MessageService` for sending messages to be displayed * Injecting `MessageService` into the `HeroService` * Displaying a message when `HeroService` fetches heroes successfully ### [](#create-messagescomponent) Create `MessagesComponent` Use `ng generate` to create the `MessagesComponent`. Copy ng generate component messages `ng generate` creates the component files in the `src/app/messages` directory and declares the `MessagesComponent` in `AppModule`. Edit the `AppComponent` template to display the `MessagesComponent`. src/app/app.component.html Copy

{{title}}

You should see the default paragraph from `MessagesComponent` at the bottom of the page. ### [](#create-the-messageservice) Create the `MessageService` Use `ng generate` to create the `MessageService` in `src/app`. Copy ng generate service message Open `MessageService` and replace its contents with the following. src/app/message.service.ts Copy import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class MessageService { messages: string[] = []; add(message: string) { this.messages.push(message); } clear() { this.messages = []; } } The service exposes its cache of `messages` and two methods: * One to `add()` a message to the cache. * Another to `clear()` the cache. ### [](#inject-it-into-the-heroservice) Inject it into the `HeroService` In `HeroService`, import the `MessageService`. src/app/hero.service.ts (import MessageService) Copy import { MessageService } from './message.service'; Declares a private `messageService` property. Angular injects the singleton `MessageService` into that property when it creates the `HeroService`. src/app/hero.service.ts Copy private messageService = inject(MessageService); This is an example of a typical _service-in-service_ scenario in which you inject the `MessageService` into the `HeroService` which is injected into the `HeroesComponent`. ### [](#send-a-message-from-heroservice) Send a message from `HeroService` Edit the `getHeroes()` method to send a message when the heroes are fetched. src/app/hero.service.ts Copy getHeroes(): Observable { const heroes = of(HEROES); this.messageService.add('HeroService: fetched heroes'); return heroes; } ### [](#display-the-message-from-heroservice) Display the message from `HeroService` The `MessagesComponent` should display all messages, including the message sent by the `HeroService` when it fetches heroes. Open `MessagesComponent` and import the `MessageService`. src/app/messages/messages.component.ts (import MessageService) Copy import { MessageService } from '../message.service'; Declare a **public** `messageService` property. Angular injects the singleton `MessageService` into that property when it creates the `MessagesComponent`. src/app/messages/messages.component.ts Copy public messageService = inject(MessageService); The `messageService` property **must be public** because you're going to bind to it in the template. Angular only binds to _public_ component properties. ### [](#bind-to-the-messageservice) Bind to the `MessageService` Replace the `MessagesComponent` template created by `ng generate` with the following. src/app/messages/messages.component.html Copy @if (messageService.messages.length) {

Messages

@for (message of messageService.messages; track message) {
{{ message }}
}
} This template binds directly to the component's `messageService`. DETAILS `@if` Only displays the messages area if there are messages to show. `@for` Presents the list of messages in repeated `
` elements. Binds the button's click event to `MessageService.clear()`. [](#add-messageservice-to-heroescomponent) Add MessageService to HeroesComponent ------------------------------------------------------------------------------------- The following example shows how to display a history of each time the user clicks on a hero. This helps when you get to the next section on Routing. src/app/heroes/heroes.component.ts Copy import { Component, OnInit } from '@angular/core'; import { Hero } from '../hero'; import { HeroService } from '../hero.service'; import { MessageService } from '../message.service'; @Component({ selector: 'app-heroes', templateUrl: './heroes.component.html', styleUrls: ['./heroes.component.css'], standalone: true }) export class HeroesComponent implements OnInit { selectedHero?: Hero; heroes: Hero[] = []; private heroService = inject(HeroService); private messageService = inject(MessageService); ngOnInit(): void { this.getHeroes(); } onSelect(hero: Hero): void { this.selectedHero = hero; this.messageService.add(`HeroesComponent: Selected hero id=${hero.id}`); } getHeroes(): void { this.heroService.getHeroes() .subscribe(heroes => this.heroes = heroes); } } Refresh the browser to see the list of heroes, and scroll to the bottom to see the messages from the HeroService. Each time you click a hero, a new message appears to record the selection. Use the **Clear messages** button to clear the message history. [](#final-code-review) Final code review --------------------------------------------- Here are the code files discussed on this page. src/app/hero.service.tssrc/app/message.service.tssrc/app/heroes/heroes.component.tssrc/app/messages/messages.component.tssrc/app/messages/messages.component.htmlsrc/app/messages/messages.component.csssrc/app/app.component.html Copy import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { Hero } from './hero'; import { HEROES } from './mock-heroes'; import { MessageService } from './message.service'; @Injectable({ providedIn: 'root', }) export class HeroService { private messageService = inject(MessageService); getHeroes(): Observable { const heroes = of(HEROES); this.messageService.add('HeroService: fetched heroes'); return heroes; } } Copy import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class MessageService { messages: string[] = []; add(message: string) { this.messages.push(message); } clear() { this.messages = []; } } Copy import { Component, OnInit } from '@angular/core'; import { Hero } from '../hero'; import { HeroService } from '../hero.service'; import { MessageService } from '../message.service'; @Component({ selector: 'app-heroes', templateUrl: './heroes.component.html', styleUrls: ['./heroes.component.css'], standalone: true }) export class HeroesComponent implements OnInit { selectedHero?: Hero; heroes: Hero[] = []; private heroService = inject(HeroService); private messageService = inject(MessageService); ngOnInit(): void { this.getHeroes(); } onSelect(hero: Hero): void { this.selectedHero = hero; this.messageService.add(`HeroesComponent: Selected hero id=${hero.id}`); } getHeroes(): void { this.heroService.getHeroes() .subscribe(heroes => this.heroes = heroes); } } Copy import { Component } from '@angular/core'; import { MessageService } from '../message.service'; @Component({ selector: 'app-messages', templateUrl: './messages.component.html', styleUrls: ['./messages.component.css'], standalone: true }) export class MessagesComponent { public messageService = inject(MessageService); } Copy @if (messageService.messages.length) {

Messages

@for (message of messageService.messages; track message) {
{{ message }}
}
} Copy /* MessagesComponent's private CSS styles */ h2 { color: #A80000; font-family: Arial, Helvetica, sans-serif; font-weight: lighter; } .clear { color: #333; background-color: #eee; margin-bottom: 12px; padding: 1rem; border-radius: 4px; font-size: 1rem; } .clear:hover { color: white; background-color: #42545C; } Copy

{{title}}

[](#summary) Summary ------------------------- * You refactored data access to the `HeroService` class. * You registered the `HeroService` as the _provider_ of its service at the root level so that it can be injected anywhere in the application. * You gave the `HeroService` `get data` method an asynchronous signature. * You discovered `Observable` and the RxJS `Observable` library. * You used RxJS `of()` to return `Observable`, an observable of mock heroes. * The component's `ngOnInit` lifecycle hook calls the `HeroService` method, not the constructor. * You created a `MessageService` for loosely coupled communication between classes. * The `HeroService` injected into a component is created with another injected service, `MessageService`. [](#hier-geht-es-weiter) → Hier geht es weiter --------------------------------------------------- ### [](#injectable-services) `@``()` services Notice that the new service imports the Angular symbol and annotates the class with the `@``()` decorator. This marks the class as one that participates in the _dependency injection system_. The `HeroService` class is going to provide an injectable service, and it can also have its own injected dependencies. It doesn't have any dependencies yet. The `@``()` decorator accepts a metadata object for the service, the same way the `@``()` decorator did for your component classes. By default, `ng generate service` registers a provider with the _root injector_ for your service by including provider metadata, that's `providedIn: 'root'` in the `@``()` decorator. When you provide the service at the root level, Angular creates a single, shared instance of `HeroService` and injects into any class that asks for it. Registering the provider in the `@` metadata also allows Angular to optimize an application by removing the service if it isn't used. To learn more about providers, see the . To learn more about injectors, see the . This is an interim code sample that allows you to provide and use the `HeroService`. At this point, the code differs from the `HeroService` in the . When Angular creates a `HeroesComponent`, the system sets the `heroService` property to the singleton instance of `HeroService`. Instead, call `getHeroes()` inside the and let Angular call `ngOnInit()` at an appropriate time _after_ constructing a `HeroesComponent` instance. `Observable` is one of the key classes in the . The shows you how to call `HttpClient.get()`, which also returns an `Observable` that emits _a single value_, an array of heroes from the body of the HTTP response. Angular The messages look better after you add the private CSS styles to `messages.component.css` as listed in one of the tabs below. You used to inject it into a component. 🦸‍♂️ 📖 [`new`keyword](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/new) [_dependency injection_](https://v16.angular.io/guide/dependency-injection) [`Injectable`](https://v16.angular.io/api/core/Injectable) [`Injectable`](https://v16.angular.io/api/core/Injectable) [`Injectable`](https://v16.angular.io/api/core/Injectable) [`Injectable`](https://v16.angular.io/api/core/Injectable) [`Component`](https://v16.angular.io/api/core/Component) [`Injectable`](https://v16.angular.io/api/core/Injectable) [`Injectable`](https://v16.angular.io/api/core/Injectable) [Providers section](https://v16.angular.io/guide/providers) [Dependency Injection guide](https://v16.angular.io/guide/dependency-injection) [final code review](https://v16.angular.io/tutorial/tour-of-heroes/toh-pt4#final-code-review) [Dependency Injection](https://v16.angular.io/guide/dependency-injection) [_ngOnInit lifecycle hook_](https://v16.angular.io/guide/lifecycle-hooks) [RxJS library](https://rxjs.dev/) [HTTP tutorial](https://v16.angular.io/tutorial/tour-of-heroes/toh-pt6) ["final code review"](https://v16.angular.io/tutorial/tour-of-heroes/toh-pt4#final-code-review) [Angular Dependency Injection](https://v16.angular.io/guide/dependency-injection) [💡Hinweise zu Kapitel 4](/tag-1/tour-of-heroes/4.-add-services/hinweise-zu-kapitel-4) [event binding](https://v16.angular.io/guide/event-binding) --- # Ergänzend zu Navigation | M335 - Mobile-Applikation realisieren [](#wie-kann-ich-zu-einer-seite-resp.-zuruck-navigieren) Wie kann ich zu einer Seite resp. zurück navigieren? ------------------------------------------------------------------------------------------------------------------ Die Navigation ist basierend auf welcher URL wir gerade haben resp. welcher Route dazu passt. Wenn die URL gewechselt wird, ändert die aktuelle Seite. Aber wir haben einige weitere Optionen um zu navigieren: Zuerst müssen wir den Angular Router importieren und in unserer Page/Component hinzufügen. Füge dazu in der Page/Component folgendes ein: navigation.page.ts Copy // Schritt 1: Angular Router importieren import { ..., inject } from '@angular/core'; import { Router } from '@angular/router'; ... export class NavigationPage implements OnInit { // Schritt 2: Router injecten private router = inject(Router) ... } Danach kann man ein normalen Angular Router **href** verwenden um auf eine andere Seite zu navigieren. `/detail` ist dabei die URL der Zielseite. navigation.page.html Copy ... oder auch im Code: navigation.page.ts Copy navigateToDetail() { this.router.navigateByUrl('/detail'); } navigation.page.html Copy [](#wie-ubergebe-ich-werte-zwischen-zwei-seiten) Wie übergebe ich Werte zwischen zwei Seiten? -------------------------------------------------------------------------------------------------- Das Array, welches der `navigate` Funktion als Parameter übergeben wird, akzeptiert die folgenden Parameter: navigation.page.ts Copy navigateToDetail() { // Der Parameter 'id' wird später weiter unten weiterverwendet this.router.navigate(['/detail', { id: itemId }]); } Die Daten sind in der `PersonDetail`\-Seite nun über die `ActivatedRoute` mit über `paramMap`zu holen: person-detail.page.ts Copy import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-person-detail', templateUrl: './person-detail.page.html', styleUrls: ['./person-detail.page.scss'], }) export class PersonDetailPage implements OnInit { private personID: number; private route = inject(ActivatedRoute); ngOnInit() { // Hier wird die Personen-ID über den Parameter 'id' geholt. this.personID = this.route.snapshot.paramMap.get('id'); } } Mehr dazu findest du auch in folgendem Tutorial: [PreviousHinweise und Zusatzaufgaben zu Kapitel 5](/tag-1/tour-of-heroes/5.-navigation/hinweise-und-zusatzaufgaben-zu-kapitel-5) [Next💡 Debugging](/tag-1/tour-of-heroes/debugging) Last updated 4 months ago 🦸‍♂️ 📖 💡 --- # Hinweise und Zusatzaufgaben zu Kapitel 5 | M335 - Mobile-Applikation realisieren [Previous5\. Navigation](/tag-1/tour-of-heroes/5.-navigation) [NextErgänzend zu Navigation](/tag-1/tour-of-heroes/5.-navigation/erganzend-zu-navigation) Last updated 4 months ago [](#ubungen) Übungen ------------------------- **Input und Output** Du hast im Kapitel 3 kennengelernt, wie man Daten in eine Child-Komponente hineingibt. Natürlich kannst du auch vom Child Events an den Parent zurückgeben. dazu verwendet man Event Emitter. Wir wollen nun die Heroes, die in der Heldenauflistung angezeigt werden, als eigene Komponente auslagern. Erstelle dafür eine Komponente, welche die Daten eines Helden als Input entgegennimmt und beim Klick einen Event an den Parent schickt mit der Id des Helden. Diese wird dann für die Navigation verwendet. Im `@for` soll nun deine Komponente angezeigt werden. **Async Pipe anwenden** Im 4. Kapitel hast du gelernt wie man Observable nutzt und was das genau ist. Wir hatten dafür eine Liste von Helden die als `Observable` zurückgeliefert wurde. Da du jetzt eine Seite erstellt hast mit den Lessons learned, wird es Zeit deine Kontaktangaben darunter zu legen. Diese sollen mittels asynchronem Aufruf geladen werden. Folgendermassen muss das aussehen: Copy contactData$: Observable = of({ company: "[DEINE FIRMA]", name: "[DEIN NAME]", email: "[DEINE EMAIL ADRESSE]", }) Es handelt sich hierbei nicht um eine Liste sondern ein einzelnes Objekt, so kannst du `@for` nicht mehr nutzen. `@if` ist eine sehr gute Option, recherchiere mal wie dies gemacht wird. Lies bitte dazu den folgenden Artikel durch: 🦸‍♂️ 📖 💡 [output()](https://angular.dev/guide/components/outputs) ![](https://m335.ict-bz.ch/~gitbook/image?url=https%3A%2F%2F1951332971-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FfaRLpqjavHkOOr8FPTA1%252Fuploads%252F8FKhd4Max4ZOfVe0BmFz%252Fimage.png%3Falt%3Dmedia%26token%3D18cfb8e5-7ae0-4087-8dbe-2711f1c38791&width=768&dpr=4&quality=100&sign=e0c65d8d&sv=2) ---