Component Architecture

Eigene Komponenten

Eine Komponente integrieren

  • wie in der Index.html: einfach mit dem Selektor als HTML Element einbinden
  • Komponenten können beliebig verschachtelt werden

Aufgabe 8.1 - weitere Komponente

  • Branch: 08_ComponentArchitecture_0
  • Erstelle in src/app einen Unterordner cinema
  • Erstelle eine cinema.component.ts mit einem cinema.component.html Template
  • Das Template enthält eine h1 Überschrift "Cinema"
  • Die app.component soll nur noch unsere Cinema Komponente enthalten
  • Denke daran, die neue Komponente im app.module.ts in Declarations hinzuzufügen

Aufgabe 8.1 - Allgemeines

  • Das UI statest Du mit npm start

Aufgabe 8.1 - Mögliche Lösung

  • Branch: 08_ComponentArchitecture_1
  • Die Lösungen bauen nun aufeinander auf
  • Wenn alles funktioniert könnt ihr immer weiter arbeiten ohne den Branch wechseln zu müssen
  • Der Lösungsbranch ist immer die Grundlage für die nächste Aufgabe
  • Wenn ihr eure Änderungen verwerfen möchtet und direkt zur Lösung wollt: git reset --hard && git checkout 08_ComponentArchitecture_1

Aufgabe 8.2 - Daten anzeigen

  • Branch: 08_ComponentArchitecture_1
  • Erstelle ein Interface movie (siehe db.json)
  • Erstelle eine neue movies Komponente. Sie hat eine Liste von Movies (aus db.json kopieren)
  • Rendere die Movies im HTML und gib alle Properties aus - die Bild-URL als img

Aufgabe 8.2 - Mögliche Lösung

  • Branch: 08_ComponentArchitecture_2
  • Wenn ihr eure Änderungen verwerfen möchtet und direkt zur Lösung wollt: git reset --hard && git checkout 08_ComponentArchitecture_2

@Input - Daten übergeben

Binding: Quelle vs. Ziel

  • es gibt immer eine Quelle und ein Ziel bei Bindings
  • das Ziel steht links vom =, die Quelle rechts
  • Das Ziel ist ein Property oder ein Event in Klammern: [prop], (event)
  • Die Quelle steht in "quelle" oder in {{quelle}}

Instanzvariablen vs. Input & Output

  • @Input() und @Output() definieren das Interface von Direktiven (und Komponenten)
  • Sie sind das Ziel der Bindings
  • Die Quelle von Bindings sind alle Instanzvariablen von Direktiven (und Komponenten)

@Input() - Beispiel

                    
                        import {Component, Input} from '@angular/core';

                        @Component({
                            selector: 'pizza'
                        })
                        export class PizzaComponent {
                            @Input()
                            pizza: Pizza;
                        }
                    
                    
                        
                        
                    
                    
                        private meinePizza: Pizza;
                    
                 

@Input() - Beispiel 2

                    
                        @Component({
                            selector: 'pizza'
                            inputs: ['pizza', 'zutaten']
                        )}
                        export class PizzaComponent {
                        }
                    
                    
                        
                        
                    
                    
                        private meinePizza: Pizza;
                        private zutaten: Zutat[];
                    
                 

Aufgabe 8.3 - @Input()

  • Branch: 08_ComponentArchitecture_2
  • Erstelle eine neue movie Komponente die einen einzelnen Film darstellt.
  • Übergebe den Film mit einem Binding an die neue Komponente.

Aufgabe 8.3 - Mögliche Lösung

  • Branch: 08_ComponentArchitecture_3
  • Wenn ihr eure Änderungen verwerfen möchtet und direkt zur Lösung wollt: git reset --hard && git checkout 08_ComponentArchitecture_3

@Output - auf Events reagieren

  • Events von Standard-Komponenten wie Buttons oder Inputs kennen wir bereits
  • Events für eigene Komponenten verhalten sich genau gleich
  • sie definieren zusammen mit den Inputs das Interface einer Komponente

@Output() - Beispiel

                    
                        import {.., Output, EventEmitter} from '@angular/core';
                        export class PizzaComponent {
                            @Output()
                            selectPizza: EventEmitter<Pizza> = new EventEmitter();
                        }
                    
                    
                        
                    
                    
                        
                        <pizza (selectPizza)="onPizzaSelected($event)">
                    
                    
                        onPizzaSelected(pizza: Pizza) { console.log(pizza); }
                    
                 

Aufgabe 8.4 - @Output()

  • Branch: 08_ComponentArchitecture_3
  • Erstelle einen Button zum Entfernen eines Films
  • Jeder Film soll einen eigenen Button haben
  • Bei einem Klick soll die Komponente ein Event werfen - die äussere Komponente soll dann darauf reagieren und den Film aus der Liste entfernen

Aufgabe 8.4 - Mögliche Lösung

  • Branch: 08_ComponentArchitecture_4
  • Wenn ihr eure Änderungen verwerfen möchtet und direkt zur Lösung wollt: git reset --hard && git checkout 08_ComponentArchitecture_4

Component Architecture

Smart / Container Components

  • haben Zugriff auf Services / den Applikations State
  • reagieren auf Events ihrer Childs
  • geben Daten von Services an die Childs weiter
  • besitzen keine Anzeigelogik - sie sind lediglich ein Container für eine Vielzahl dummer Komponenten

Dumb / Presentation / Stateless Components

  • haben keinen Zugriff auf Services / den Applikations State
  • bekommen Daten von aussen per @Input
  • geben Events per @Output nach draussen
  • enthalten die Anzeigelogik
  • sind sehr einfach reusable

Wann nutze ich was?

  • generell sollte pro Seite die Komponente, die die gesamte Seite umspannt, smart sein
  • wenn eine Komponente direkt per Route (URL) erreichbar ist, so muss sie smart sein
  • Immer «dumm» beginnen und nur wenn nicht anders möglich «smart» werden.

Wie schneide ich Komponenten

  • ähnlich wie bei Klassen: Single Responsibility
  • wird eine Komponente zu gross, dann in mehrere kleine aufteilen
  • wenn deine Komponente 2 Dinge machen muss...
  • ... dann mach 3 Komponenten daraus ;-)
  • Eine die das Feature wrapped, und für jeden Teil eine weitere Komponente

Was sind die Vorteile dieses Konzeptes?

  • Testbarkeit
  • Wiederverwendbarkeit
  • Keine Seiteneffekte - einfacher Datenfluss
  • Klares Konzept

Lifecycle Hooks

OnInit

  • die Hook-Methode heisst ngOnInit()
  • die Komponente sollte das Interface OnInit implementieren
  • wird einmalig nach dem Konstruktor aufgerufen
  • für komplexere Initialisierungen, HTTP Calls etc.
  • Inputs sind bereits initialisiert

OnInit - Beispiel

                    
                        import {Component, OnInit} from "@angular/core";

                        @Component({
                            selector: 'pizza',
                            templateUrl: './pizza.component.html'
                        })
                        export class PizzaComponent implements OnInit {

                            pizze: Pizza[];

                            ngOnInit() {
                                this.pizze = getPizzeFromService();
                            }
                        }
                    
                

Aufgabe 8.5 - OnInit

  • Branch: 08_ComponentArchitecture_4
  • Identifiziere eine Smart und eine Dumb Komponente
  • Füge in der Smart Komponente einen OnInit Hook hinzu
  • Führe dort aus, was aus deiner Sicht Sinn ergibt ;-)

Aufgabe 8.5 - Mögliche Lösung

  • Branch: 08_ComponentArchitecture_5
  • Wenn ihr eure Änderungen verwerfen möchtet und direkt zur Lösung wollt: git reset --hard && git checkout 08_ComponentArchitecture_5
  • Smart Component: MoviesComponent
  • Dumb Component: MovieComponent

Zeit für Fragen