Wie funktioniert ein Observable in Angular?

Ehrlich gesagt glaube ich, dass jeder, der einen Artikel mit solch einer Überschrift liest, bereits weiss, was ein Observer ist. Und das Thema Observer ist im Grunde eine absolut einfache Sache. In diesem Artikel erkläre ich kurz, was Observer sind und zeige es dann an einem Codebeispiel.

Was sind Obersables?

Stimmt, die heissen ja Observables, gar nicht Observer, verdammt. Ja ne, einen dooferen Namen hätten sie denen wohl nicht geben können. Sogar im Titel dieses Abschnitts habe ich es unabsichtlich falsch geschrieben.

Das Prinzip ist ganz einfach und auch unter Publish Subscribe und anderen Namen bekannt – Oder vorallem von Instagram und anderen Medienplattformen. Wir haben zum Beispiel eine ganz coole Band wie Sum 41, richtige Rockstars, die übrigens gerade jetzt ein neues Album herausbringen. Und dann haben wir Fans dieser Rockstars. Und die Fans wollen natürlich wissen, was die Rockstars so treiben, was sie zum Frühstück essen, wo das nächste Konzert ist etc. Und die Rockstars schicken natürlich gerne alle Informationen an die Fans, sobald diese bekannt sind.

PS: Ich wollte eigentlich die Band Falconer nehmen, die ein echter Geheimtipp von mir ist und die ich momentan im Repeat-Forever Modus rauf und runter höre, aber die sind so unbekannt, dass ich lieber Sum 41 genommen habe. Die kennen viele wohl noch aus ihrer Jugendzeit 🙂

Ja und die Rockstars ist also unser Observable und die Fans sind die Observer (auf Deutsch: Beobachter). Und jeder Fan klickt natürlich auf Instagram oder Youtube auf „Follow“, um immer über alles informiert zu sein – das wäre der Subscribe-Mechanismus.

But why?

Ja das frage ich mich auch jeden Abend vor dem Einschlafen: Warum braucht es eigentlich Observables? Also bei mir war es einfach so, dass in meinem aktuellen Projekt Observables verwendet werden und ich darum gezwungenermassen lernen muss, was dahinter steckt.

Na gut, schauen wir mal, was auf der offiziellen Seite steht… blabla… blabla… ah ja, hier steht es: Es geht darum, dass Komponenten miteinander kommunizieren wollen, und dass es asynchron passiert, also nicht blockierend.

Gut, irgendwie finde ich das auch komisch, zu erklären, warum es Observables braucht. Frag mal einen Sum 41 Fan, warum er die neuesten News von der Band haben will… Na logisch, er will halt immer auf dem aktuellen Stand sein. Und dass es nicht blockiert, wie wir Informatiker zu sagen pflegen, ist auch logisch: Kein Fan möchte permanent auf der Sum 41-Homepage sein und aktualisieren drücken, sondern die Nachrichten bequem erhalten, sobald es Neuigkeiten gibt.

Aber wem erkläre ich das eigentlich? Wir alle werden ja mittlerweile todgespamt von allen möglichen Apps, die uns Push-Notifications zusenden.

Aber gut, genug geschwatzt, bevor mir auch noch die letzte Handvoll Leser weglaufen, bringe ich lieber mein Code-Beispiel.

Lass uns ein Observable in Angular bauen

Das Beispiel ist todlangweilig (Super Einstieg, um Spannung aufzubauen, Anm. des Reviewers):

  • Wir haben eine Liste von Studenten in der Rockband (Observable)
  • und die Fans (Observer) erhalten entweder die Informatikstudenten oder die Chemiestudenten als Liste serviert.

Zuerst brauchen wir ein Beispielprojekt. Sie wissen schon, wie man ein Projekt erstellt, oder? In Java fängt auch nicht jedes Tutorial damit an, wie man ein Projekt in Eclipse erstellt. Aber vielleicht ist das ein Ritual bei Angular-Tutorials, also falls Sie es nicht wissen würden: Sie müssen mittels npm angular installieren und dann folgenden Befehl ausführen:

ng new echt-cooles-projekt

Okay haben wir diese Hürde genommen. Als nächstes erstellen wir eine einfache Model-Klasse von einem Studenten. Im Ordner „src -> app“ können Sie die folgende Datei student.model.ts erstellen:

export class Student {
   id: number;
   name: string;
   fach: string;
 }

Nun kommt der eigentliche Observer. Diesen packen wir gleich neben das Student-Model in eine Datei student.service.ts und die sieht folgendermassen aus:

import { Injectable } from '@angular/core';
import { Student } from './student.model';
import { Observable, Observer } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class StudentService {

  outsideObserver;

  informatikStudenten: Student[] = [{
    id: 1,
    name: 'Fritz',
    fach: 'Informatik'
  },
  {
    id: 2,
    name: 'Hans',
    fach: 'Informatik'
  }];

  chemieStudenten: Student[] = [{
    id: 3,
    name: 'Peter',
    fach: 'Chemie'
  }];

  constructor() {

    // 5000 steht für 5000 Millisekunden bzw. Sekunden
    setTimeout(() => {
      console.log('wechsle jetzt zu Chemiestudenten mit next');
      this.outsideObserver.next(this.chemieStudenten);
    }, 7000);

    // Das reine Löschen der Daten von aussen aktualisiert die Daten im Client nicht
    // Erst nach einem erneuten next() Aufruf
    setTimeout(() => {
      console.log('lösche jetzt die Informatikstudenten ohne next aufzurufen');
      this.informatikStudenten = [];
      console.log(this.informatikStudenten);
    }, 10000);
  }

  public getStudents(): any {
    const studentsObservable = new Observable(observer => {
      this.outsideObserver = observer;

      observer.next(this.informatikStudenten);

      setTimeout(() => {
        console.log('Jetzt schicke ich nochmals die Informatikstudenten mit next');
        observer.next(this.informatikStudenten);
      }, 12000);

      setTimeout(() => {
        console.log('sende nochmals Chemiestudenten mit next');
        observer.next(this.chemieStudenten);
      }, 16000);

      setTimeout(() => {
        console.log('Verarbeitung complete');
        // Nach einem Complete funktioniert kein error() und kein next() mehr
        observer.complete();
      }, 18000);

      // Nach einem Error funktioniert kein next() mehr
      setTimeout(() => {
        observer.error('Verdammt dieser Fehler bringt nach dem Complete gar nichts!!!');
      }, 20000);

    });

    return studentsObservable;
  }
}

Häh? Ich versteh nichts! Ich werde lieber Kebabverkäufer!

Pah, das kann gar nicht sein, ich habe das Beispiel extra einfach und verständlich gewählt!

Also gut, ich erkläre, was ich gemacht habe:

a) Die Klasse beinhaltet zwei Listen von Studenten: Informatikstudenten und Chemiestudenten. Diese wollen wir später auf der Homepage sehen.

b) Die Rockband selber findet sich in der Methode getStudents();. Dort sieht es auch komplizierter aus, als es tatsächlich ist. Ich habe einige setTimeout()’s eingebaut, welche nach und nach ausgeführt werden, damit wir das Verhalten des Observables untersuchen können. Die könnte man auch weglassen.

Das Wichtigste ist aber einfach das erstellen des Observables mit new.

Die ganze Geschichte mit dem outsideObserver können Sie eigentlich ignorieren. Das habe ich nur eingebaut, um zu zeigen, dass man das Observables auch speichern und später an einer anderen Stelle wiederverwenden kann – Was ich nämlich in keinem anderen Tutorial im Internet gefunden habe.

Die drei Methoden des Observables

Und was man dann noch wissen muss: Ein Observables – so mühsam ich dieses Wort auch finde – hat drei Methoden, die es verwenden kann:

  1. Mit next() werden alle Fans, die ein Abo haben, mit den neuesten Infos versorgt – Welche Infos das auch immer sein mögen, das kann man ja selber definieren.
  2. Mit error() kann man das Passwort des Fans auslesen. Neeeiiiinnn natürlich kann man damit eine Fehlermeldung schmeissen.
  3. Mit complete() kann man die Rockstars quasi in Rente schicken. Nach einem complete() geht next() oder error() nicht mehr, das Observable ist dann abgeschlossen. Ist übrigens optional, der Informationsfluss kann durchaus einfach offenbleiben.

Ja gut, jetzt haben wir sogar noch ein wenig Theorie gelernt.

Was ganz wichtig bei der ganzen Sache ist: Observer abonnieren zwar Informationen von den Observables, wenn ein Observable die Daten intern ändert, kriegt der Observer das aber noch nicht mit! Erst wenn der Observable die Methode next() aufruft, werden die Daten an alle Observer mit einem Abonnement verschickt.

Einen neuen Fan erstellen – Vielleicht sogar einen Groupie!

In unserem neuen Projekt können wir gleich die Datei app.component.ts für den Fan beziehungsweise den Observer verwenden. Dieser muss der Rockband mitteilen, dass sie jetzt alle News haben möchte. Das sieht dann so aus:

import { Component, OnInit } from '@angular/core';
import { Student } from './student.model';
import { StudentService } from './student.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  students: Student[] = [];
  studentsObserver;

  constructor(private studentService: StudentService) {}

  ngOnInit() {
    this.studentsObserver = this.studentService.getStudents();
  }

  getStudentData() {
    this.studentsObserver.subscribe((studentsData: Student[]) => {
      this.students = studentsData;
    });
  }

}

Hier sehen wir, wie das StudentService-Observable in den Konstruktor depency-injected wird (erklären Sie diesen Satz mal Ihrer Mutter…).

In der Methode getStudentData() wird dann effektiv das Abonnement abgeschlossen und auch gleich mitgegeben, wohinein die geschickten Daten gespeichert werden sollen.

Ich habe das ganze bewusst ein wenig auseinandergenommen um zu zeigen, dass der Code an verschiedenen Stellen in der Datei liegen kann.

Die Methode getStudentData() wird dabei von einem Button in der Datei app.component.html aufgerufen. Das sieht so aus:


<p>Öffne die Entwicklerkonsole mit F12 um die Meldungen zu sehen</p>
<p>Bitte rasch einmalig auf den Button drücken, um die Informatikstudenten zu sehen</p>

<button (click)="getStudentData()">Zeig die Studenten an</button>

<div class="col-md-3 col-xs-6" *ngFor="let student of students">
  <p>{{ student.id }}</p>
  <p>{{ student.name }}</p>
  <p>{{ student.fach }}</p>
  <hr>
</div>

Ja, als Konsequenz ist es halt so, dass Sie beim Start der Applikation den Knopf drücken müssen, damit das Abonnement abgeschlossen wird. Aber das Leben ist nun mal eine seriöse Angelegenheit. Da fällt mir gleich ein Zitat von einem „Falconer-Lied ein, das lautet:

There are too many stones and life ist just made of glass

Hmm im Lied klingt das irgendwie eindrucksvoller als einfach so geschrieben. Das Lied heisst übrigens „Busted to the floor„. Witziger Fact: Beim Erfinder dieses Titels handelt es sich um dieselbe Person, die auch den Observables den Namen gegeben hat!

Okay, wo waren wir?

Ah ja, also alles reinkopiert und dann Server gestartet mit

ng serve

dann http://localhost:4200/ aufgerufen und gleich den angezeigten Button geklickt.

Dann noch mit F12 die Entwicklerkonsole aufgemacht und schon können Sie schön nachvollziehen, wie die Studenten nacheinander vom Observable an den Observer geschickt werden.

Gut, ich gebe zu, ich habe heute Morgen wohl eine Clownsmannschaft gefrühstückt, aber es ist auch sauheiss hier und ausserdem Freitag, da muss man sich eine grosse Portion schlechten Humor bewahren. Wenn Ihnen das nicht passt schreiben Sie mir doch bitte einen Kommentar unten hin, das würde mich freuen!

In dem Sinne viel Glück mit der weiteren Angular-Programmierung!

Werbeanzeigen

2 Gedanken zu „Wie funktioniert ein Observable in Angular?

  1. Aciderella

    Ich finde den Artikel genial 😀 zudem verstehe ich jetzt endlich diesen „Scheiß“. Eigentlich easy 😉 Ich danke dir vielmals. Gruß Franzi

    Liken

    1. Invest

      Danke schön! Ich probiere immer, die Tutorials so witzig wie möglich zu gestalten, dass man sich nicht langweilt dabei. Vielleicht übertreib ich es auch ab und an aber egal, so bin ich halt 🙂

      Liken

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s