Angular b-nova To Do List Tutorial

02.09.2020 Tom Trapp
Mobile angular typescript

Angular

Angular ist ein sehr beliebtes, Client-seitiges JavaScript-Framework um sogenannte Single-Page-Applications (SPA) zu erstellen. Spricht man von einer Single-Page-Application ist ein bestimmtes Pattern oder ein Aufbau einer Website gemeint. Einfach gesagt gibt es nur ein HTML-Dokument, die dynamischen Daten werden Client-seitig verändert oder mittels einer REST-API von einem Backend abgerufen. Dadurch entsteht eine sehr schlanke und performante Webpage mit einer recht kleinen Bundle Size. Ein weitere grosser Vorteil von Angular ist, dass die Applikation vollständig responsive ist, und sie sowohl auf einem mobilen Gerät oder auf dem Desktop benutzt werden kann.

Die Codebasis von Angular bildet die Sprache TypeScript, welche im Gegensatz zu "normalem" JavaScript typed (Englisch für typensicher) ist. Angular selbst bietet aber nicht nur eine Framework für SPAs, sondern eine ganze Platform für Entwickler mit beispielsweise Generatoren, Architektur-Konzepte, fertige Lösungen und zahlreichen nützlichen Werkzeugen.

Angular vs. AngularJS

Immer wieder hört man Angular aber auch AngularJS, was genau ist da der Unterschied und was haben die beiden JavaScript-Libraries gemeinsam? Kurz gesagt ist Angular der Nachfolger von AngularJS, wobei der Kern komplett neu entwickelt wurde und man neu auf TypeScript setzt. Heutzutage gibt es von AngularJS zwar eine LTS Version, das Opensource Projekt verlinkt aber selbst zur "neuen Angular Version" - dem normalen Angular.

Getting Started - b-nova ToDo Liste

In diesem kurzen Tutorial lernen wir Angular Hands On kennen und erstellen eine einfache ToDo-Liste. Dafür brauchen Sie lediglich ein Terminal, eine IDE und einen Browser. Wir verwenden Microsoft Visual Studio Code und Google Chrome auf einem OSX System.

1. Vorbereitung

Um Angular betreiben zu können benötigten wir NodeJS, laden Sie die letzte LTS Version von NodeJS herunter und installieren Sie diese.

Profi Tipp: Wenn Sie OSX nutzen, NVM erleichtert Ihnen die Arbeit mit NodeJS und NPM.

Angular bietet ein eigenes CLI mit vielen nützlichen Commands an. Führen Sie folgende Zeile in Ihrem Terminal aus.

npm install -g @angular/cli

2. Neue Angular App erstellen

Mit folgendem Command nutzen wir die Angular CLI, um ein neues Angular Projekt zu erstellen. Seit Angular 10 gibt es einen sogenannten strict mode, welcher für strikteres Typechecking und generell einen kleinlicheren Compiler sorgt. Generell sollte diese Option (mit dem Paremeter --strict) aktiviert sein, um potenzielle Fehler und Bad Practises frühzeitig zu erkennen.

Führen Sie folgendes Command aus:

ng new b-nova-todo-list --strict --routing=false --skip-git

Anschliessend müssten Sie Ihr Stylesheet für das Projekt wählen, der Einfachheit halber nutzen wir CSS. Nach kurzer Wartezeit ist unser Angular Projekt fertig erstellt und wir wechseln in den neuen Ordner b-nova-todo-list.

Mit dem Command code . öffnen wir den aktuellen Ordner als Visual Studio Code Workspace.

3. Folder Struktur eines Angular Projektes kennenlernen

Im nachfolgenden Screenshot sind kurz die wichtigsten Bestandteile eines Angular Projektes beschrieben.

Wo ist nun aber der Einstiegspunkt?

Unterhalb des Ordners src finden Sie eine index.html Datei, dies ist die Datei, welche vom Browser geladen wird. Angular selbst nutzt die sogenannte app-root Direktive in der index.html als Einstiegspunkt.

4. Der erste Start unserer Anwendung

Nun wird es spannend, wir lassen das erste Mal unser Boot ins Wasser und zwar mit folgendem Command:

ng serve --open

Hierbei sorgt --open dafür, dass die Anwendung direkt im Browser geöffnet wird. Anschliessend sollten Sie unter http://localhost:4200/ Ihre ersten Angular App mit einer Demo Seite sehen.

5. Der ersten Changes an unserer Anwendung

Als ersten einfachen Change wollen wir das HTML Element title verändern, dazu öffnen wir die src/index.html, diese sollte wie folgt aussehen:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>BNovaTodoList</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
</body>
</html>

Sie sehen den Title BNovaTodoList in Ihrem Browser oben im Tab, diesen wollen wir nun zu "b-nova ToDo List" ändern.

Sehen Sie die Änderung bereits in Ihrem Browser? Sehr gut, dies passiert ohne weitere Commands, da Angular Änderungen in den Sourcen erkennt und diese automatisch erneut compiled (Ahead-of-time (AOT) compilation) und im Browser die Page erneut ladet (Hot Code Replacement).

Anschliessend wollen wir das Demo HTML entfernen und durch unser eigenes kleines Grundgerüst ersetzen. Ersetzen Sie den Inhalt aus src/app/app.component.html mit folgenden HTML:

<div class="app">
  <header>
    <h1>b-nova ToDo List</h1>
  </header>
</div>

Nun sollten Sie lediglich die Überschrift "b-nova ToDo List" in Ihrem Browser sehen.

6. Die erste Angular Komponente

Grundsätzlich besteht eine Angular Komponente aus vier Teilen bzw. vier Files.

name.component.html

Das Template mit dem HTML-Markup der Komponente.

name.component.css

Die CSS-Styles der Komponente.

name.component.ts

Das TypeScript File der Komponente, es enthält die Deklaration sowie der Logik der Komponente.

name.component.spec.ts

Dieses File ist ebenfalls ein TypeScript File und beinhaltet Anweisungen fürs Testing der Komponente.

Aber nun wollen wir unsere erste Angular Komponente erstellen, glücklicherweise liefert uns auch hier die Angular CLI ein Command:

ng generate component components/ToDos

Profi Tipp: Wie bei vielen Commands gibt es hier ein Shorthand Syntax ng g c components/ToDos.

Sie sollten nun folgende Struktur unterhalb von src/app sehen:

├── app
│   ├── app.component.css
│   ├── app.component.html
│   ├── app.component.spec.ts
│   ├── app.component.ts
│   ├── app.module.ts
│   └── components
│       └── to-dos
│           ├── to-dos.component.css
│           ├── to-dos.component.html
│           └── to-dos.component.ts

Das to-dos.component.spec.ts File können wir löschen, da wir dies nicht benötigen.

Schauen wir uns nun das Template unserer neuen ToDos Komponente ansehen wir, dass der Text to-dos works ausgegeben werden sollte. Leider können wir diesen aber im Browser noch nicht sehen, da wir die Komponente noch nicht eingebunden haben.

Eine Komponente wird mittels einer Direktive im HTML eingebunden, der Name der Direktive muss dem Property "selector" in der Component Declaration im to-dos.components.ts File entsprechen. In unserem Fall ist der Selektor als "app-to-dos", diesen nutzen wir nun in der src/app/app.component.html Datei um die Komponenten einzubinden:

<div class="app">
    <header>
        <h1>b-nova To Do List</h1>
    </header>
    <app-to-dos></app-to-dos>
</div>

Nun sehen wir im Browser "to-dos works!", somit haben wir unsere erste Komponente erfolgreich erstellt und eingebunden!

7. Das ToDo Array

Nun möchten wir ein Array von ToDos definieren und anzeigen können. Bevor wir aber die eigentlichen Items definieren müssen wir ein Datenmodell konzeptionieren und anlegen. Dafür erstellen wir unterhalb von src/app ein Ordner mit dem Namen "models" und darin eine Datei "ToDo.ts".

Ein klassisches ToDo Item besteht aus einem Text und einem Flag, ob es bereits abgearbeitet wurde.

export class ToDo {
    content:string = '';
    completed:boolean = false;
}

Nun importieren wir das ToDo Model in unserer ToDos Component (TypeScript File) und definieren ein neues Array mit einigen ToDo Items.

import { Component, OnInit } from '@angular/core';
import { ToDo } from './../../models/ToDo'
@Component({
  selector: 'app-to-dos',
  templateUrl: './to-dos.component.html',
  styleUrls: ['./to-dos.component.css']
})
export class ToDosComponent implements OnInit {

  toDos:ToDo[] = [];
  constructor() { }

  ngOnInit(): void {
    this.toDos = [
      {
        content: 'Get tea',
        completed: true
      },
      {
        content: 'Write blog post',
        completed: false
      },
      {
        content: 'Publish blog post',
        completed: false
      }
    ]
  }
}

Nun haben wir drei ToDo Items in das toDos Array geschrieben, dies haben wir in der sogenannten ngOnInit Methode gemacht.

In Angular gibt es sogenannte Component Lifecycle Hooks welche uns erlauben, bestimmte Logik in einer bestimmten Phase einer Komponente auszuführen. Der ngOnInit Hook wird beispielsweise während Angular diese Komponente initialisiert aufgerufen. Dies ist ein guter Platz um Daten o. ä. für die Komponente bereitzustellen und zu laden.

Aber wie kommen die ToDo Items nun in unser Template und werden zu HTML? Angular nutzt doppelt geschweifte Klammern als sogenannte Template String, so können Variablen aus der TypeScript Klasse der Komponente im HTML ausgegeben werden.

In unserer to-dos.component.html Datei wollen wir über alle toDos loopen und diese inklusive einer Nummerierung ausgeben. Dies erreichen wir mit der *ngFor Direktive, wir weisen jedes Mal das aktuelle Item aus toDos der Variable toDo sowie den aktuellen Index der Variabel i zu.

<div class="todos">
  <div class="todo" *ngFor="let toDo of toDos; let i = index">
    <div class="index">{{ i }}</div>
    <div class="content">{{ toDo.content }}</div>
    <button class="delete">Delete</button>
  </div>
</div>

Anschliessend fügen wir noch etwas CSS hinzu. Sie finden den CSS Code am Ende dieses Blogbeitrags.

Nun haben wir erfolgreich unsere erste Komponente definiert und Daten aus dem TypeScript File werden im HTML Template ausgegeben. Unsere ToDo-Liste sollte wie folgt aussehen:

8. Abhaken von ToDo Items

Bevor wir uns um das eigentlich Abhaken kümmern wollen wir ein visuelles Feedback implementieren. Da wird im ToDo Model bereits das Field completed haben, können wir dies in Templates Strings im HTML Template nutzen, in Kombination mit dem Ternary Operator.

Unser ".todo" Div sieht nun wie folgt aus, wir können die toDo Variable des *ngFor Loops auf gleicher Ebene direkt verwenden. Somit können wir die Klasse done bei completed = true Items ausgeben.

<div class="todo {{ toDo.completed ? 'done' : '' }}" *ngFor="let toDo of toDos; let i = index">

Nun wollen wir aber vorhandene ToDos einfach per Klick auf completed stellen, dafür benötigen wir eine Methode in unsere TypeScript Component.

  toggleCompleted(indexToUpdate:number):void {
    this.toDos.map((item,index) => {
      if (index == indexToUpdate) {
        item.completed = !item.completed;
      }
    })
  }

Die Methode toggleCompleted wird mit einem indexToUpdate aufgerufen und toggelt bei dem ToDo Element an diesem Index das Feld completed.

Nun müssen wir aber noch im HTML auf das "Click" Event reagieren, dies macht man in Angular aber nicht mit der aus JavaScript bekannten "onClick" Funktion. In Angular gibt es sogenannte Event Bindings, man kann mit normalen Klammern auf bestimmte Event (in unserem Fall click) reagieren.

    <div class="content" (click)="toggleCompleted(i)">{{ toDo.content }}</div>

Unser .content Div ruft so die Methode toggleCompleted mit dem Index i bei einen Click auf und wir können ToDos abhaken!

9. ToDos löschen

Schliesslich kommen wir zum angenehmsten Part einer ToDo-Liste, das Löschen von ToDo-Einträgen.

Dafür benötigten wir wie im oberen Schritt eine Methode, welche einen indexToDelete entgegennimmt und diesen löscht.

  deleteToDo(indexToDelete:number):void {
    this.toDos = this.toDos.filter((item,index) => index !== indexToDelete);
  }

Ausserdem benötigten wir, ebenfalls wie im vorherigen Schritt, ein click Event auf dem Delete Button, welches unsere deleteToDo Funktion aufruft.

    <button class="delete" (click)="deleteToDo(i)">Delete</button>

Wie Sie nun beim Löschen eines Items sehen rendert Angular von selbst direkt das angepasste Array, ohne weitere Changes.

Nun haben wir die Möglichkeit ToDo-Items anzuschauen, abzuhaken und zu löschen!

10. Eintrag hinzufügen - Two Way Data Bindung

Zu guter Letzt wollen wir natürlich noch die Möglichkeit schaffen, ToDos erfassen zu können.

Hierfür benötigen wir zuerst ein HTML Form mit einem Textfeld und einem Submit Button.

  <form>
    <input type="text" class="todo-input" name="inputToDo" placeholder="Enter ToDo..."/>
    <input type="submit" value="Add ToDo" class="todo-submit"/>
  </form>

Anschliessend wollen wir die Logik in unserer ToDos TypeScript Component implementieren. Hierfür benötigen wir eine neue Variable und eine Methode.

  inputToDoText:string = '';
  ...
  ...

  addToDo(){
    this.toDos.push(
        {
            content: this.inputToDoText,
            completed: false
        }
    )
    this.inputToDoText = "";
}

So haben wir nun eine Variable inputToDoText vom Type String, welche den Wert den Input Feldes beinhalten soll und eine Methode zum Hinzufügen des ToDo-Items. In der Methode addToDo fügen wir mittels push ein neues ToDo Element zum toDos Array hinzu und resetten anschliessend die Variable inputToDoText erneut.

Aber wie kommt der Wert des Textfeldes nun in die Variable? Angular bietet auch hierfür eine praktische Funktionalität, genannt Two Way Data Binding. Mit der sogenannten ngModel Direktive wird ein Input Feld mit einer Variable verknüpft, Änderungen werden in beide Richtungen immer direkt aktualisiert. Dadurch hat man den Wert der Variable immer im Input Feld ebenso wie jede Änderung des Wertes im Input Feld automatisch in der Variable aktualisiert wird.

Um dieses Feature nutzen zu können müssen wir aber erst das FormsModule in unserer Anwendung importieren, dies machen wir in der src/app/app.module.ts Datei.

import { FormsModule } from '@angular/forms';
...
imports: [
    ...
    FormsModule
]

Zuerst importieren wir das FormsModule und fügen es dann in das imports Array hinzu, so können wir es nun in unserer Applikation nutzen. Wie Sie gesehen haben, ist in dieser Datei auch unsere ToDos Component referenziert, dies ist ebenfalls nötig (wurde in unserem Fall von der Angular CLI gemacht).

Das Two Way Data Binding wird mit normalen und eckigen Klammern im HTML File gemacht, neu sieht unser Textfeld dann wie folgt aus:

    <input [(ngModel)]="inputToDoText" type="text" class="todo-input" name="inputToDo" placeholder="Enter ToDo..."/>

Zu guter Letzt müssen wir noch unseren Button mit der addToDo Methode verknüpfen, dies machen wir über ein Event Binding auf das submit Event des Forms:

  <form (submit)="addToDo()">

Herzlichen Glückwunsch, Sie haben erfolgreich eine b-nova To Do List erstellt. Sie können nun ToDo Items ansehen, abhaken, löschen und neue hinzufügen.

Das Endergebnis sollte wie folgt aussehen:

Sie brauchen einem kompetenen und zuverlässigen Partner für Ihr Angular Projekt? Kontaktieren Sie uns!

CSS Code to-dos.component.css

.todo {
  display: flex;
  padding: 10px 15px;
  background-color: #fff;
  border-bottom: 1px solid #ddd;
}

.todo:nth-child(even) {
  background-color: #eee;
}

.todo:last-of-type {
  border-bottom: 0;
}

.todo .index {
  flex: 1 1 50px;
}

.todo .content {
  flex: 1 1 100%;
}

.todo.done .content {
  text-decoration: line-through;
}

.todo-input {
  display: block;
  width: 100%;
  padding: 10px 15px;
  appearance: none;
  border: none;
  background-color: #f3f3f3;
  margin-top: 15px;
  font-size: 20px;
  outline: none;
}

.todo-submit {
  display: block;
  width: 100%;
  max-width: 200px;
  appearance: none;
  border: none;
  outline: none;
  background: none;

  background-color: #fe4880;
  color: #fff;
  margin: 15px auto;

  padding: 10px 15px;
  font-size: 18px;
  font-weight: 700;
}

Tom Trapp - problem solver, innovator, athlete. Tom prefers to work on modern software all day long and attaches great importance to objectively clean, lean code.