Angular: Skalierbare und moderne Web Frontends

Nachdem wir für unsere Anwendung das Backend implementiert haben, widmet sich dieser Artikel der Erstellung eines Frontends. Hierzu verwenden wir das Framework Angular und erstellen eine Single-Page Application – SPA.

Eine Single-Page Application wird wie eine klassische Website von einem Server ausgeliefert. Dabei wird jedoch nicht nur eine Seite ausgeliefert, sondern die komplette Anwendung, welche meist aus viel JavaScript-Logik besteht. Nach dem Laden läuft die Anwendung anschließend vollständig im Browser des Anwenders. Die von der Anwendung benötigten Daten werden anschließend von einer REST-API geladen und dargestellt. Die Erstellung so einer API haben wir uns mit Azure und AWS bereits angeschaut.

Aufsetzen der Entwicklungsumgebung

Angular ist ein Framework, welches selber auf Node.js aufsetzt. Aus diesem Grund müssen wir zuerst Node.js installieren. In diesem Beispiel installieren wie Node.js über Brew auf einem Mac. Über die offizielle WebSite stehen auch für andere Betriebssysteme Installationsdateien zur Verfügung.

> brew install node
> npm install -g @angular/cli
> ng version
     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
     
 
Angular CLI: 9.1.0
Node: 12.16.1
OS: win32 x64

Nachdem wir nun Angular installiert haben, können wir unser Projekt anlegen. Dazu benutzen wir ein Befehl von Angular, welcher uns ein funktionsfähiges leeres Projekt erstellt.

> ng new todo-app
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? CSS
...

Wichtig ist hier, dass die Frage nach Angular routing mit Yes beantwortet wird, da die App später den Anwender auf verschiedene Seiten leiten wird. Durch diese Option wird das Projekt für die Verwendung dieses Features bereits vorbereitet. Die Auswahl nach dem CSS-Format kann nach persönlichen Vorlieben entschieden werden.

Anschließend erhalten wir folgende Projektstruktur:

todo-app/                               # App-Verzeichnis
├── e2e/                                # Hier werden End-to-End-Tests implementiert
├── node_modules/                       # Beinhaltet alle installierten Node.js-Module für das Projekt
├── src/                                # Beinhaltet die Implementierung der App
│   ├── app/                            # In diesem Ordner werden die Komponente, Services, etc, abgelegt
│   │   ├── app-routin.module.ts        # Konfiguration der Navigationsrouten innerhalb der App (welche Komponente ist für welche URL zuständig)
│   │   ├── app.component.css           # CSS-Styles, welche ausschließlich für die aktuelle Komponente gültig sind
│   │   ├── app.component.html          # HTML-Template-Code, welche beim Ausführen der Komponente angezeigt wird
│   │   ├── app.component.spec.ts       # Unit-Tests für die Komponente
│   │   ├── app.component.ts            # "Code-Behind"-Datei, welche die Business-Logik der Komponente beinhaltet
│   │   └── app.module.ts               # Root-Konfigurations-Datei der App, in welcher alle verwendeten Node.js-Module, App-Komponente, etc. registriert werden
│   ├── assets/                         # Beinhaltet Dateien wie Bilder
│   ├── environments/
│   │   ├── environment.prod.ts         # Konfigurationsdatei für den produktiven Betrieb
│   │   └── environment.ts              # Konfigurationsdatei für das Entwickeln
│   ├── ...
│   ├── index.html                      # Root-Datei, welche Aufgerufen wird, wenn der Anwender die SAP aufruft
│   ├── main.ts                         # Einstiegspunkt für die App (Muss in der Regel nicht geändert werden)
│   └── styles.css                      # Datei für globale CSS-Regeln, welche für die gesamte App gelten
├── ...
├── angular.json                        # Konfiguration des Projektes, welche u.a. z.B. die Build-Konfigurationen beinhaltet
└── package.json                        # Konfigurationsdatei, welche angibt, welche Node.js-Module in welcher Version verwendetet werden

Zu guter letzt installieren wir noch ein paar weitere Packete, welche wir im Verlauf der Implementierung verwenden werden …

> npm install --save bootstrap jquery popper.js

… und referenzieren anschließend  noch die entsprechenden CSS-Dateien.

{
  "projects": {
    "todo-app": {
      "architect": {
        "build": {
          "options": {
            "styles": [
              "src/styles.css",
              "node_modules/bootstrap/dist/css/bootstrap.min.css"
            ],
            "scripts": [
              "node_modules/jquery/dist/jquery.min.js",
              "node_modules/popper.js/dist/umd/popper.min.js",
              "node_modules/bootstrap/dist/js/bootstrap.min.js"
            ]
          }
        }
      }
    }
  }
}

Nun können wir das Projekt starten und im WebBrowser die generierte Start-Seite sehen. Das leicht defekte Design liegt an der Einbindung von Bootstrap und kann ignoriert werden, da wir die Seite im nächsten Schritt ersetzen werden.

> ng serve --open

Anzeige einer Liste von Tasks

Als erstes implementieren wir die Start-Seite unser Anwendung. Diese soll eine Liste aller vorhandenen TODO-Aufgaben anzeigen. Dabei können die benötigten Dateien manuell per Hand angelegt werden oder wir nutzen Angular und lassen uns die Dateien generieren.

> ng generate component task-list
 
CREATE src/app/task-list/task-list.component.html (24 bytes)
CREATE src/app/task-list/task-list.component.spec.ts (643 bytes)
CREATE src/app/task-list/task-list.component.ts (286 bytes)
CREATE src/app/task-list/task-list.component.css (0 bytes)
UPDATE src/app/app.module.ts (485 bytes)

Die generierten Dateien haben dabei folgenden Nutzen:

  • task-list.component.html: Hier wird der HTML-Code, also die Anzeige der Liste implementiert.
  • task-list.component.ts: Hier wird die Logik zum Laden der Tasks implementiert, damit diese anschließend zur Anzeige zur Verfügung stehen.
  • task-list.component.spec.ts: Hier können Unit-Tests implementiert werden, welche die Korrektheit der Logik prüft.
  • task-list.component.css: Hier können Komponenten-spezifische Style-Regeln hinterlegt werden, welche nur für diese Komponente gelten.
<ul class="list-group">
    <li class="list-group-item text-nowrap" *ngFor="let task of tasks">
        <a [title]="'Details für ' + task.name">
            <strong>{{ task.name }}</strong>
        </a>
          
        <span *ngIf="task.priority === 1" class="badge badge-danger">High</span>
        <span *ngIf="task.priority === 2" class="badge badge-info">Middle</span>
        <span *ngIf="task.priority === 3" class="badge badge-light">Low</span>
    </li>
</ul>
import { Component, OnInit } from '@angular/core';
 
import { TaskService } from '../task.service';
 
@Component({
  selector: 'app-task-list',
  templateUrl: './task-list.component.html',
  styleUrls: ['./task-list.component.css']
})
export class TaskListComponent implements OnInit {
  tasks;
 
  constructor(private taskService: TaskService) { }
 
  ngOnInit(): void {
    this.taskService.getTasks()
      .subscribe(tasks => (this.tasks = tasks));
  }
}
// ...
 
import { RouterModule } from '@angular/router';
 
// ...
 
@NgModule({
  // ...
  imports: [
 
    // ...   
 
    RouterModule.forRoot([
      { path: '', component: TaskListComponent }
    ])
  ],
  // ... 
})
// ...

Implementierung der Kommunikation mit REST-API

Durch durch die Art und Weise, dass die SPA im Browser des Anwenders ausgeführt wird, kann sie nicht direkt auf die Datenbank zugreifen. Aus diesem Grund wird eine SPA fast immer in Verbindung mit einer REST-API verwendet, welche einen kontrollierten Zugriff auf die Daten bereitstellt.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
 
import { Observable } from 'rxjs';
 
import { Task } from './task';
 
@Injectable({
  providedIn: 'root'
})
export class TaskService {
 
  baseUrl = 'https://exmaple.com/api';  // URL to web api
 
  constructor(
    private http: HttpClient) {}
 
  /** GET tasks from the server */
  getTasks (): Observable {
    return this.http.get(`${this.baseUrl}/task`);
  }
 
}
// ...
import { HttpClientModule } from '@angular/common/http';
 
// ...
 
@NgModule({
  // ...
  imports: [
    // ...   
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    FormsModule,
    HttpClientModule,
    // ...   
  ],
  // ... 
})
// ...

Die Implementiert der zugehörige API kann in den anderen beiden Artikeln nachgelesen werden, wo diese einmal mit Azure Web Service und einmal mit Amazon AWS implementiert wurde.

Deployment der SPA als Azure App-Service

Auch für das Bauen einer veröffentlichbaren App bringt Angular fertige Befehle mit. Dieser kompiliert und komprimiert den Code, sodass beim späteren deployen nur wenige kleine Dateien hochgeladen werden müssen. Dies hat zusätzlich den Vorteil, dass die Anwendung beim Anwender schneller lädt, da der Browser ebenfalls nur wenige kleine Dateien herunterladen muss um die Anwendung zu starten.

> ng build --prod
 
Generating ES5 bundles for differential loading...
ES5 bundle generation complete.
 
chunk {0} runtime-es2015.1eba213af0b233498d9d.js (runtime) 1.45 kB [entry] [rendered]
chunk {0} runtime-es5.1eba213af0b233498d9d.js (runtime) 1.45 kB [entry] [rendered]
chunk {2} polyfills-es2015.690002c25ea8557bb4b0.js (polyfills) 36.1 kB [initial] [rendered]
chunk {3} polyfills-es5.9e286f6d9247438cbb02.js (polyfills-es5) 129 kB [initial] [rendered]
chunk {1} main-es2015.af5b6cb4ca52ef1efbf6.js (main) 376 kB [initial] [rendered]
chunk {1} main-es5.af5b6cb4ca52ef1efbf6.js (main) 456 kB [initial] [rendered]
chunk {4} styles.66057ddcee1a0c134465.css (styles) 146 kB [initial] [rendered]
chunk {scripts} scripts.4f7d6bdd9aa2a2ef7f2f.js (scripts) 165 kB [entry] [rendered]
Date: 2020-04-16T13:22:11.289Z - Hash: c755857ecfed044fd8c0 - Time: 43419ms

Als nächstes können wir die App nach Azure deployen. Sofern Visual Studio Code verwendet wird, geschieht dies über das Azure Plugin recht einfach. Dazu auf dem entsprechenden AppService mit der rechten Maus klicken und Deploy to Web App… auswählen.

In dem nun erscheinenden Dialog navigieren wir zu dem dist-Ordner in unserem Projekt und laden diesen hoch.

Die Anwendung kann nun über die URL der Azure Wep App aufgerufen werden.

Anzeige der Aufgaben in einer ToDo Liste

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.