Angular-Komponente
Eintrag zuletzt aktualisiert am: 07.11.2016

Bei Angular ist keine direkte Manipulation des HTML-DOM vorgesehen, sondern eine Trennung nach dem Model-View-Controller (
MVC)-Muster: Der Entwickler definiert im View sogenannte
Templates, die statische HTML-Tags mit dynamischen Inhalten aus dem Controller verbinden. Diese Mischung gestattet das Framework durch benutzerdefinierte HTML-Syntaxelemente ("
Direktiven"). In
AngularJS 1.x ist eine
Direktive an sich schon ein komplexes Gebilde (vgl. [2]), das durch die Controller und Scopes weitere Herausforderungen birgt.
In der neuen Version hat
Google diese Architektur stark vereinfacht: Ein benutzerdefiniertes Tag in einer Seite wird durch eine mit @Component dekorierte
Komponentenklasse repräsentiert. Der selector (siehe Listing 1) legt den Tagnamen fest. Die mit der
Komponente verbundene Vorlage ("
Template") kann der Webentwickler direkt als Zeichenkette in der Eigenschaft template angeben, was sich aber nur bei sehr kurzen HTML-Blöcken anbietet. Ansonsten verweist man mit templateUrl auf eine .html-Datei (siehe Listing 2). Sofern man beides angibt, gewinnt die Eigenschaft template. Den bisher notwendigen Verweis der Vorlage auf den Controller mit ng-Controller gibt es nicht mehr. Anders als beim Vorgänger ist es in Angular 2.x nicht mehr möglich, dass eine
Komponente ein komplettes Tag ersetzt; das
Template der
Komponente liefert immer nur neuen Inhalt für das Tag.
Eine
Komponente kann auch eigene
CSS-Vorgaben (Inline oder als eigenständige Datei) besitzen, die allein für die Vorlage dieser
Komponente gelten, vorausgesetzt, der Entwickler bezieht nicht mit den Zusätzen :host bzw. /deep/ auch die über- und unterordneten
Komponenten explizit mit ein. In der Vorlage für die Flugliste in Listing 2 sieht man mit <Flugdetail> wieder ein benutzerdefiniertes Tag. Dies ist eine untergeordnete
Komponente, die über Eigenschaften und Ereignisse mit der übergeordneten
Komponente interagiert. Durch die Dekoratoren @Input() und @Output() muss der Entwickler klar festlegen, welche Eigenschaften und Ereignisse für andere
Komponenten im Zugriff sind. Zur Ereigniskommunikation bietet Angular 2.x einen eigenen Event Emitter.
Eine
Komponente löst im Laufe ihres Lebenszyklus diverse Ereignisse aus, in die der Entwickler sich einhängen kann. Listing 1 zeigt dies am Beispiel des Ereignisses OnInit, das aus der Sicht von
TypeScript eine Klasse ist, die wie alle anderen Typen zu Beginn mit import { OnInit, … } from '@angular/core' einzubinden ist. Weitere Ereignisse stehen im Zusammenhang mit der
Datenbindung (ngOnChanges, ngDoCheck).
Das Angular-Entwicklungsteam verwendet in den Beispielen (siehe [
https://github.com/angular/angular.io/tree/master/public/docs/_examples]) die
Namenskonvention (siehe [
https://angular.io/docs/ts/latest/guide/style-guide.html#!#naming]), dass die Namen aller
Komponentenklassen und deren zugehörige
Templatedateien auf dem Wort "Component" enden. Dieses lange Suffix lenkt aber vom eigentlichen Namen ab, was gerade in großen Projekten nicht erwünscht sein kann. Auch wenn das Beispiel hier klein ist, verzichtet es auf diese ablenkenden Suffixe.
Listing 1: Komponentenklasse für Flugliste
// Angular Library-Importe
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import {
Observable } from 'rxjs/
Observable';
// Importe für eigene Klassen
import { Flug } from '../Modell/Flug'
import { FlugService } from '../Services/FlugService'
import { Flugdetail } from '../Flugdetail/Flugdetail'; // für Event
@Component({
selector: 'Flugliste',
templateUrl: 'App/Flugliste/Flugliste.html',
providers: [FlugService] // lokale
Registrierung eines Dienstes für DI
})
export class Flugliste implements OnInit {
constructor(private flugService: FlugService,
private router: Router) { // hier wird FlugService und Router injiziert
}
status: string;
flugSet: Flug[]
ngOnInit() {
this.flugService.getAll(daten => { //
Callback
this.flugSet = FlugService.flugSet;
this.status = this.flugSet.length + " Flüge geladen.";
});
}
Loeschen(flug: Flug) {
this.flugService.delete(flug); // Dienst aufrufen
this.status = `Flug ${flug.FlugNr} gelöscht!`;
}
Aendern(flug: Flug) {
let link = ['/edit', flug.FlugNr];
this.router.navigate(link);
}
// Event-
Handler für Nachricht von untergeordneter
Komponente
onFlugGeloescht(flug: Flug) {
this.status = `Flug ${flug.FlugNr} gelöscht!`;
this.flugSet = this.flugService.flugSet; //
Datenbindung aktualisieren }
}
Listing 2: Angular-Vorlage für die Flugliste
<ul>
<li *ngFor="let f of flugSet ; let istGerade = even; let i = index">
<span [ngClass]="{'text-primary': istGerade, 'text-info': !istGerade}">
<span class="badge">{{i+1}}: Flug #{{f.FlugNr | fixedLenNumber:3}}</span>
<Flugdetail [flug]="f" (flugGeloeschtEvent)="onFlugGeloescht($event)"></Flugdetail>
<button typ="info" *ngIf="f.FreiePlaetze>0" (click)="Aendern(f)">Ändern</button>
<button typ="warning" [disabled]="f.FreiePlaetze<=0 || f.AbflugOrt=='Essen'" (click)="Loeschen(f)">Löschen</button>
</span>
</li>
</ul>