Sie sind hier

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>