Die Struktur einer Anwendung zu verstehen ist zentral, um strukturiert zu entwickeln und sich auch in unbekannten Projekten zurechtzufinden. Daher werden wir uns in diesem Abschnitt die wichtigsten Bestandteile einer Angular-Anwendung anschauen, verstehen, wofür man sie braucht und wie man sie einfach automatisch erzeugt. In die tiefen Details steigen wir dann in den folgenden Kapiteln ab.

Dieser Artikel ist Teil der Angular Basic Tutorials. Zum Einstieg findest du hier den Code zum letzten Kapitel.

Ziel

Am Ende dieses Theorieteils sind alle für ein einfaches Angular-Projekt relevanten Bestandteile bekannt und wir wissen, was eine Komponente, ein Service und das AppModule sind. Außerdem ist bekannt, wofür man die package.json-Datei benötigt.

Die genannten Dateien sind im folgenden Screenshot gelb markiert:

vs code plugins

Komponenten

Komponenten sind der zentrale Baustein einer Angular-Anwendung. Hinter jeder Seite, die im Browser angezeigt wird, liegt eine Angular-Komponente. Komponenten können und sollen geschachtelt werden. Alle Komponenten liegen innerhalb des Ordners app. Die Basiskomponente ist die app.component.ts. Sie liegt direkt unter /app. Neue Komponenten werden in einem eigenen Ordner darunter angelegt.

Jede Angular-Komponente besteht in der Regel aus drei Teilen:

.component.html

Diese HTML-Datei beinhaltet die graphischen Elemente, im Prinzip Standard-HTML erweitert um verschiedene Angular-Direktiven und -Keywords. Verwendet werden kann sowohl reines HTML <div>, <ul>, <li>, <table>... als auch Komponentenbibliotheken, die graphische und funktional aufbereitete HTML-Komponenten liefern. Meine persönliche Empfehlung ist, vor Projektstart zu evaluieren, ob eine Komponentenbibliothek verwendet werden kann, da diese meist ein durchdachtes und einheitliches UI/UX-Konzept repräsentiert. Komponentenbibliotheken betrachten wir im nächsten Kapitel.

<!-- HTML code -->
<section id="main">
  <div>
   <h1 id="title">{{ title }}</h1>
    {{summary}}
  </div>
</section>

.component.scss

Die Datei ist das Stylesheet für die Komponente. Hier definierte CSS-Klassen sind nur für die zugehörige Komponente gültig.

.component.ts

Diese Datei enthält die Logik für die Komponente. Hier wird mit Services kommuniziert, die die Daten für diese Komponente liefern, Click-Handler von Buttons, Inputs… implementiert und vieles mehr. Im Laufe dieses Tutorials werden wir mehrere Komponenten anlegen und diese nach und nach um Funktionalitäten erweitern. Diese Datei definiert auch den HTML-Selektor, über den eine Komponente in eine andere Komponente eingebunden werden kann. Dabei kann durch die mehrfache Verwendung eines Selektors eine Komponente wiederverwendet und mehrfach hinzugefügt oder aber auch in anderen Komponenten verwendet werden. Die übergeordnete Komponente nennt man dann Parent und die innerhalb des Parents verwendete Komponente nennt man Child.

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'AngularTutorial';
  summary = 'This is an awesome tutorial'
}

Erzeugen einer neuen Komponente:

ng g c NAME

Best Practices

  • Komponenten sollten so klein wie möglich und so groß wie nötig sein. Das macht sie lesbarer, testbarer und wartbarer. Eine gute Richtlinie ist hier das Single-Responsibility-Prinzip.
  • Komplexe Logik sollte in Services ausgelagert werden
  • Je weniger komponentenspezifisches CSS, desto besser. Somit werden Inkonsistenzen im Aussehen der Anwendung vermieden.
  • Erzeuge eine logische Ordnerstruktur für deine Komponenten, z.B. anhand der Entität, die sie anzeigen.
  • Vermeide es, mehrere Komponenten in die gleiche Datei zu schreiben. Das macht sie schwerer auffindbar und erschwert ein späteres Refactoring.

Services

Services kümmern sich um die Datenverwaltung der Anwendung. Über den HttpClient kann ein HTTP-Service erzeugt werden, der die Kommunikation mit dem Backend ermöglicht. Außerdem wird wiederverwende Logik auf Daten in Services ausgelagert. Services sollten immer auf der Ebene der Entität liegen, die sie verwalten.

Erzeugen eines neuen Services:

ng g s NAME

Best Practices

  • Services sollten so klein wie möglich und so groß wie nötig sein, damit sie lesbar, testbar und wartbar sind. Eine gute Richtlinie ist hier das Single-Responsibility-Prinzip
  • Ein HTTP-Service implementiert die Backend-Anbindung für genau eine Entität.

Module

Ein Angular-Projekt basiert auf Modulen. Module ermöglichen es:

  • Aspekte einer Anwendung logisch zu trennen
  • einzelne Bereiche im Browser nachzuladen, um die Ladezeiten für den Anwender zu minimieren
  • die Wiederverwendbarkeit zu erhöhen

Dabei gibt es immer ein Basis-Modul, das app.module.ts, welches beim Start der Anwendung als Root dient. In diesem Tutorial werden wir zunächst nur mit dem app.module.ts arbeiten. Im späteren Verlauf werden wir uns anschauen, wie und warum man mehrere Module in einem Angular-Projekt verwendet.

Das app.module.ts beinhaltet einige zentrale Strukturen, die wir stets anpassen müssen, wenn wir neue Komponenten erzeugen oder neue Dependencies hinzufügen.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { MaterialModule } from './material/material.module';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    MaterialModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

declarations

In den declarations werden alle Komponenten aufgelistet, die Bestandteil eines Modules sind. Ist eine Komponente in keinem Modul gelistet, kann sie im Projekt nicht verwendet werden. Erzeugt man eine neue Komponente, sollte sie daher sofort in einem Modul deklariert werden. Zu Beginn haben wir nur eine einzige Komponente in der Liste: die AppComponent.

imports

Hier müssen alle Abhängigkeiten eingetragen werden, die wir in unserem Projekt verwenden wollen. Gleichzeitig sollte darauf geachtet werden, dass keine unnötigen Abhängigkeiten hier gelistet werden. Je größer unsere Anwendung ist, desto mehr Zeit braucht sie zum Laden im Browser.

Hat man eine neue Dependency installiert und die Anwendung im Code klappt nicht wie erwartet, sollte man überprüfen, ob man den richtigen Import hier eingetragen hat.

bootstrap

Hier wird die Root-Komponente unserer Anwendung eingetragen. Normalerweise ist dies die AppComponent, die auch nicht geändert wird. (Nur enthalten im AppModule)

package.json

Da wir NPM als Dependency Manager verwenden, haben wir eine package.json-Datei, in der alle Abhängigkeiten eingetragen werden, die unser Projekt benötigt.

Es wird dabei zwischen dependencies (Abhängigkeiten, die zur Laufzeit des Projektes benötigt werden) und devDependencies (Abhängigkeiten, die nur zur Entwicklungszeit benötigt werden, wie bspw. Bibliotheken zum Testen, Typings, …) unterschieden. Zusätzlich wird hier die Version der App gemanagt und es können spezifische Skripte definiert werden.

{
  "name": "angular-tutorial",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~13.2.0",
    "@angular/cdk": "^13.3.4",
    "@angular/common": "~13.2.0",
    "@angular/compiler": "~13.2.0",
    "@angular/core": "~13.2.0",
    "@angular/forms": "~13.2.0",
    "@angular/material": "^13.3.4",
    "@angular/platform-browser": "~13.2.0",
    "@angular/platform-browser-dynamic": "~13.2.0",
    "@angular/router": "~13.2.0",
    "rxjs": "~7.5.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~13.2.2",
    "@angular/cli": "~13.2.2",
    "@angular/compiler-cli": "~13.2.0",
    "@types/jasmine": "~3.10.0",
    "@types/node": "^12.11.1",
    "jasmine-core": "~4.0.0",
    "karma": "~6.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.1.0",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "~1.7.0",
    "typescript": "~4.5.2"
  }
}

Hinzufügen einer neuen Dependency:

npm i PACKAGE_NAME --s

Hinzufügen einer neuen DevDependency:

npm i PACKAGE_NAME --saveDev

styles.scss

Ist das globale Stylesheet der Anwendung. Alle CSS-Klassen, die hier eingetragen werden, sind für alle Komponenten der Anwendung gültig.