Welches JavaScript Framework passt zu mir? – Angular

Dies ist der dritte Post der Blog-Serie “Welches JavaScript Framework passt zu mir?”. Dieser Post behandelt Angular und liefert eine Einführung zu dem mächtigen Framework von Google.

Einführung

Angular ist ein Framework zur komponentenbasierten Erstellung von dynamischen Weboberflächen. Die Entwicklung von Angular begann im Jahr 2009 durch Miško Hevery – einem Entwickler von Google. Bis zur Version 1.0 vergingen trotz wachsender Aufmerksamkeit und Beliebtheit drei Jahre. Am 16. Juni 2012 wurde AngularJS in Version 1.0 veröffentlicht. Bereits 2014 wurde mit Angular 2 ein komplettes Rework von AngularJS angekündigt. Um weitere Verwirrung mit Versionsnummern zu reduzieren, wurde beschlossen, dass Angular Version 1.x mit “AngularJS” und alles mit einer höheren Version als “Angular” referenziert wird. Schon vor dem Release von Angular im Jahr 2016 gab es diverse Kontroversen, da die neue Version drastische Änderungen (auch sehr viele breaking changes) beinhaltete. Die Versionsnummer 3 wurde aufgrund eines Missverständnisses übersprungen und so wurde Version 4 im März 2017 veröffentlicht und war zu Version 2 abwärtskompatibel. Im November 2017 wurde dann die aktuelle Version 5 veröffentlicht. Google plant jedes halbe Jahr, eine neue, abwärtskompatible Version von Angular zu veröffentlichen, weshalb die Version 6 für März/April 2018 angekündigt ist.

Angular Applikationen werden mit TypeScript entwickelt. TypeScript ist ein typisiertes Superset von JavaScript, dessen Syntax gängigen Programmiersprachen wie C# und Java ähnelt, was Entwicklern einen leichteren Einstieg verschafft und für bessere Codequalität sorgt.

Komponenten

Mit Angular unterteilt man eine Oberfläche in viele, möglichst kompakte Komponenten. Diese Komponenten können an anderen Stellen in der Applikation oder sogar applikationsübergreifend wiederverwendet werden. Daher sollten die Komponenten möglichst unabhängig von ihrem Kontext sein. Die Unabhängigkeit hat zusätzlich die Vorteile der Kapselung und der erhöhten Testbarkeit, da keine Abhängigkeiten beachtet werden müssen. Das Angular Team hat eine Art Kodex aufgestellt, anhand dessen erklärt wird, warum Angular so ist wie es ist:

  • Entkopplung von DOM Manipulationen und Applikationslogik erhöht die Testbarkeit des Codes
  • Testen ist genauso wichtig wie das Entwickeln der Anwendung
  • Die Testbarkeit hängt stark von der Codestrukturierung ab
  • Entkopplung von Frontend und Backend erlaubt paralleles Arbeiten und Wiederverwendung auf beiden Seiten
  • Es hilft enorm, wenn nur ein Framework verwendet werden muss, um die GUI zu erstellen, die Geschäftslogik zu schreiben und zu testen
  • Ziel ist es, häufige Aufgaben trivial und schwierige Aufgaben möglich zu machen

Eine Komponente besteht in Angular meist aus vier verschiedenen Dateien:

  • <name>.component.css
  • <name>.component.html
  • <name>.component.spec.ts
  • <name>.component.ts

In der CSS-Datei werden die Style Anweisungen für die Komponente definiert. Hierbei ist zu beachten, dass Angular Kapselung forciert. Alle Styleanweisungen in dieser CSS-Datei gelten ausschließlich für die Komponente. Die HTML-Datei enthält das Template der Komponente. In der .spec.ts-Datei kann die Komponente mit automatisierten Tests versehen werden. Die wohl komplexeste Datei ist die .ts-Datei. Sie enthält die Logik der Komponente und verknüpft alle Dateien miteinander.

@Component({
    selector: 'app-hello-world',
    templateUrl: './helloworld.component.html',
    styleUrls: ['./helloworld.component.css']
})
export class HelloWorldComponent {
	greeting: String = "hello world"
}

In der TypeScript-Datei wird eine Klasse initialisiert und mit einer Annotation (@Component) versehen. In der Annotation wird die grundlegende Konfiguration der Komponente definiert. In diesem Beispiel kann die Komponente über das Schlüsselwort templateUrl in anderen Komponenten referenziert werden. Innerhalb der Klasse kann nun die Logik implementiert werden, die anschließend aus den Templates heraus aufgerufen werden kann.

Data Binding

Angular unterstützt 2-Wege-Databinding. Das heißt, dass die Daten im Template mit den Daten im Controller synchronisiert werden. Ein ganz simples Template der HelloWorldComponent könnte so aussehen:

<span>
	{{greeting}}
</span>

Dieses Beispiel zeigt nur ein einfaches Databinding, da das Template keinen Einfluss auf den Wert hat, sondern nur den Wert des Controllers liest. Mithilfe der doppelt geschweiften Klammer wird hier auf das Property greeting der Komponente zugegriffen. Das Property ist mit “hello world” initialisiert. Dementsprechend gibt diese Komponente lediglich “hello world” aus.

<input [(ngModel)]="username"/>

Ein 2-Wege-Databinding kann mit sogenannten Models realisiert werden. In diesem Beispiel wird das Eingabefeld mit dem Property username verknüpft. Sobald sich der Wert von username im Controller ändert, wird der Wert im Eingabefeld geändert. Zusätzlich wird der Wert im Controller geändert wenn der Benutzer den Wert im Eingabefeld ändert.

Formulare

Angular unterstützt von Haus aus 2-Wege-Databinding und hat zusätzlich noch eine spezielle Implementierung für Formulare. Im Nachfolgenden wird ein Beispiel gezeigt, wie ein Formular mit einer Komponente verknüpft werden kann. Das Beispiel zeigt ein Formular zum Registrieren von Benutzern.

userform.component.html

<form [formGroup]="form" action='' (ngSubmit)="submit()">
  <h4>{{title}}</h4>
  <label>Vorname</label>
  <span>
    <input type="text" formControlName="vorname" />
  </span>
  <label>Nachname</label>
  <span>
    <input type="text" formControlName="nachname" />
  </span>
  <label>Email</label>
  <span>
    <input type="email" formControlName="email" />
  </span>
  <label>Passwort</label>
  <span>
    <input type="password" formControlName="passwort" />
  </span>
  <button type="submit" [disabled]="! form.valid">{{submitText}}</button>
</form>

userform.component.ts

@Component({
    selector: 'app-userform',
    templateUrl: './userform.component.html',
    styleUrls: ['./userform.component.css']
})
export class UserformComponent implements OnInit {
    form: FormGroup;

    constructor(private userService: UserService, private fb: FormBuilder) {
        this.form = fb.group({
            "vorname": ["", Validators.required],
            "nachname": ["", Validators.required],
            "email": ["", Validators.compose([Validators.required, Validators.email])],
            "passwort": ["", Validators.required]
        });
    }

    submit() {
        if (!this.form.valid) {
            return;
        }
        let values = this.form.value;
        let user: IUser = {
            vorname: values.vorname,
            nachname: values.nachname,
            email: values.email,
            passwort: values.passwords.passwort,
            isEditing: false
        }
        // Benutzer registrieren...
        this.form.reset();
    }
}

Im HTML-Template wird ein Formular erstellt. Wichtig ist, dass das Formular mit dem Attribut {formGroup} versehen wird. Der Wert des Attributs entspricht dem Namen des Attributs in der TypeScript-Datei – in diesem Fall form. Mit (ngSubmit) kann eine Methode definiert werden, die beim Abschicken des Formulars aufgerufen wird. Die Eingabefelder des Formulars werden mit einem Attribut formControlName versehen, dessen Wert sich ebenfalls in der TypeScript-Datei wiederfindet.

In der TypeScript-Datei wird das Formular initialisiert und mittels eines FormBuilders aktiviert. Hierzu wird auf das FormGroup Attribut referenziert, das im Template mit {formGroup} gekennzeichnet ist. Mit dem FormBuilder wird eine FormGroup erstellt, indem die einzelnen, im Template definierten formControlNames aufgelistet werden. Zusätzlich können hier Validatoren für die einzelnen Felder angegeben werden. Beispielsweise ist die E-Mail-Adresse notwendig und außerdem soll sichergestellt werden, dass es sich tatsächlich um eine E-Mail-Adresse handelt.

Die angegebenen Validatoren ruft Angular dann jedes Mal auf, wenn sich ein Wert in einem Eingabefeld ändert und setzt anhand der Validatoren den Zustand des Formulars. Wenn beispielsweise eine invalide E-Mail-Adresse im Eingabefeld steht, ist das Attribut form.valid mit dem Wert false belegt. Sobald sich das Eingabefeld ändert und die E-Mail-Adresse valide ist, enthält form.valid true.

Routing

Angular wird mit einem Router geliefert. Der Router reagiert auf Browsernavigationen wie die Eingabe einer URL, das Klicken von Links und die Verwendung der Browserhistorie. Eine Komponente kann mit einer URL verknüpft werden, sodass bei einer URL Änderung auf die entsprechende URL die definierte Komponente angezeigt wird. Zusätzlich kann eine manuelle Navigation im Code durchgeführt werden, wodurch z. B. Redirects realisiert werden können. Der Angular Router unterstützt diverse Events, auf die reagiert werden kann:

Angular Router Events

Angular Router Events


Die Applikation kann entsprechend auf die Events reagieren und zum Beispiel Inhalte speichern, bevor der Benutzer die Seite verlässt oder gar die Navigation komplett verhindern.

Testen

In Angular wird meist mit den beiden Frameworks Jasmine und Karma getestet. Jasmine liefert Methoden, um einfach Unit Tests zu schreiben, Karma ist der Test Runner, der die geschriebenen Tests ausführt. Angular unterscheidet zwei Arten von Tests: Isolierte Unit Tests und Tests mit den Angular Testing Utilities. Die Dokumentation von Angular sieht vor, nur für Services isolierte Unit Tests zu schreiben. Man kann zwar ebenfalls Komponenten mit isolierten Unit Tests prüfen, jedoch wird dadurch nicht deutlich wie sich die Komponente zusammen mit Angular verhält. Daher sollten für Komponenten besser Tests mit den Angular Testing Utilities geschrieben werden, da diese auch das Zusammenspiel zwischen Angular und den Komponenten verprüfen.

Short URL for this post: https://wp.me/p4nxik-2Wl
This entry was posted in Web as a Platform and tagged , , , . Bookmark the permalink.

Leave a Reply