Während der Corona Pandemie haben viele Firmen und Mitarbeiter Home-Office für sich entdecket. Dadurch fiel auf, dass nicht jeder Angestellte einen eigenen Arbeitsplatz braucht. Mittlerweile bieten die Firmen stattdessen ein Shared-Desk Modell. Das bedeutet, dass sich die Mitarbeiter flexibel die Arbeitsplätze teilen. Häufig kann man sich aber seinen Wunscharbeitsplatz schon vorab reservieren. Diese Arbeitsplatz Buchungsfunktion hat mich schon länger gereizt. Besonders interessant finde ich die Möglichkeit in Angular eine Reactive Form zu erstellen, welche auch einen Büroplan beinhaltet. In der Angular Reactive Form SVG gibt es dabei die Möglichkeit einen Arbeitsplatz aus dem Büroplan anzuklicken bzw. farblich zu markieren. Sobald ein Arbeitsplatz ausgewählt wurde soll die Form valid werden und der Schreibtisch somit buchbar. Nachfolgend wird Schritt für Schritt die Umsetzung erklärt.
Das könnte dich auch interessieren: Wie man in Angular eine PUSH Architektur programmiert.
Liste der Komponenten
- Entwicklungsumgebung (z.B. VS Code)
- Angular CLI
- SVG File
Erstellung eines Büroplans
Ich würde beinahe behaupten, dass die Erstellung des Büroplans der schwerste Teil der Aufgabe war. Der Büroplan kann auf unterschiedliche Weisen erstellt werden. Wichtig dabei ist, dass es sich um eine SVG Datei handeln muss. Ich habe hierfür Adobe Xd genutzt und mit Linien, Rechtecken und Kreisen gearbeitet. Das Ergebnis könnte schöner sein, aber es reicht um die Funktionalität zu demonstrieren. Du kannst meinen Plan benutzen oder auch einen eigenen erstellen.
Nachdem das SVG Bild erstellt wurde, müssen die Bereiche die einen Arbeitsplatz darstellen zu einer Gruppe zusammengefasst werden. Ich habe den Gruppe noch gleich die Klasse „seat“ zugeteilt. Das sieht dann so aus:
<g class="seat" > <g id="table-0" transform="translate(19 59)"> <rect width="66" height="35" stroke="none" /> <rect x="2.5" y="2.5" width="61" height="30" fill="none" /> </g> <g id="screen-0" transform="translate(31 84)"> <rect id="fill" width="42" height="4" stroke="none" /> <path d="M0,1.5h42M39.5,0v4M42,2.5h-42M2.5,4v-4" fill="none" clip-path="url(#clip)" /> </g> <g id="chair-0" transform="translate(40 30)"> <ellipse cx="12.5" cy="11" rx="12.5" ry="11" stroke="none" /> <ellipse cx="12.5" cy="11" rx="10" ry="8.5" fill="none" /> </g> </g>
Der gezeigte Codeausschnitt entspricht einem Arbeitsplatz, wie beispielsweise der in dem roten Kreis hier:
Angular Reactive Form erstellen
Um den Büroplan in eine reactive Form einzubinden, erstellen wir ein neues Angular Projekt. Ich habe mich bei dem Projekt vom deutschen YouTuber Unleashed Design inspirieren lassen: https://www.youtube.com/watch?v=NOuVxP7FxBc. Allgemein kann ich seine Videos sehr empfehlen! Man lernt bei ihm immer wieder neue Tipps und Tricks, besonders im Umgang mit Angular.
Wir brauchen für die Reactive Angular Form SVG das ReactiveFormsModule. Anschließend erstellen wir eine FormGroup und FormControl Felder. Die FormGroup habe ich reservationForm getauft. Sie beinhaltet ein Feld für den Namen, ein Feld für den Wochentag und ein Feld für den Platz den man buchen möchte.
public reservationForm: FormGroup = new FormGroup({ name: new FormControl('', [Validators.required], []), weekday: new FormControl('', [Validators.required], []), seat: new FormControl('', [Validators.required], []), });
Um sich das Formular anzeigen lassen zu können ist etwas html Code notwendig:
<form [formGroup]="reservationForm"> <label for="name"> Your name:</label> <input id="name" type="text" formControlName="name" placeholder="Name" /> <label for="weekday"> Enter the reservation day:</label> <input id="weekday" type="text" formControlName="weekday" placeholder="Monday" /> </form>
Zur besseren Demonstration macht es Sinn sich auch den Wert des Formulars anzeigen zu lassen:
<p>Values: {{ reservationForm.value | json }}</p> <p>Form valid: {{ reservationForm.valid }}</p>
Seats Komponente für die Angular Form SVG erstellen
Um ein Custom Formular Feld zu bauen wird als erstes eine neue Komponente benötigt. Hier können wir gleich einen Tipp von Unleashed Design nutzen. Angular bietet nämlich die Möglichkeit SVG Bilder als templateUrl einzubinden. Wir löschen die seats.component.html und ersetzen diese durch unseren SVG Büroplan mit dem Namen seats.component.svg. In der seats.component.ts können wir dadurch die templateUrl durch die SVG Datei ersetzen:
selector: 'app-seats', templateUrl: './seats.component.svg', styleUrls: ['./seats.component.sass'],
Das Custom Formular Feld kann jetzt in die reactive Form eingebunden werden:
<app-seats id="seat" formControlName="seat"></app-seats>
In der Sass Datei kann der SVG Büroplan beliebig angepasst werden. Farben, Strichdicken, und vieles mehr können hier eingestellt werden. Ich habe es so angepasst, dass der Büroplan rot wird, wenn das SVG Formularfeld invalid wird:
$successColour: #19e619 :host &.ng-touched, &.ng-dirty &.ng-invalid svg .wall, .seat stroke: red svg margin: 25px 0 25px 0 .wall stroke: black fill: #fff stroke-width: 5 .separators stroke: black fill: none stroke-width: 5 .separate-room stroke: black fill: none stroke-width: 5 .seat stroke: grey cursor: pointer fill: #fff transition: all 250ms stroke-width: 4 &.selected fill: $successColour stroke-width: 1
ControlValueAccessor hinzufügen
Wir können zwar schon unseren Büroplan in der Reactive Form sehen, aber er ist noch ohne Funktion. Um das zu ändern wird ein ControlValueAccessor benötigt. Der ControlValueAccessor dient als Schnittstelle für eine benutzerdefinierte Formularsteuerung, die sich in Angular Formulare integrieren lässt. Der ControlValueAccessor verlangt nach diesen vier Methoden:
interface ControlValueAccessor { writeValue(obj: any): void registerOnChange(fn: any): void registerOnTouched(fn: any): void setDisabledState(isDisabled: boolean)?: void }
Mehr Informationen zum ControlValueAccessor finden sich in der Doku: https://angular.io/api/forms/ControlValueAccessor
Damit die seats.component.ts Komponente Zugriff auf den VALUE_ACCESSOR erhält müssen wir folgenden Provider konfigurieren:
providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SeatsComponent), multi: true, }, ],
Variablen und Methoden für den Angular Forms ControlValueAccessor
Der nächste Schritt sieht komplizierter aus, als er eigentlich ist. Im Prinzip handelt es sich um feste Variablen und Methoden, die jedes custom Formularfeld benötigt.
Jedes Custom reactive Form braucht die nachfolgenden Variablen. Die Variable onTouched, damit das Formularfeld später zu touched werden kann. Außerdem onChange und die statische Variable touched als boolean, um zu wissen ob das Ganze schon touched ist. Ein Formularfeld kann auch disabled sein, daher wird ebenfalls die statische Variable isDisabled als boolean benötigt. Selbstverständlich braucht jedes custom Formular auch einen value.
public onTouched = () => {}; public onChange = (value: string) => {}; public touched: boolean = false; public isDisabled: boolean = false; public value: string = '';
Jedes Custom Reactive Form braucht auch die Möglichkeit diese Variablen zu verändern. Hier kommt die bereits im vorherigen Abschnitt besprochenen ControlValueAccessor Methoden zum Einsatz. Zum einen die writeValue Funktion, die der value Variable einen neuen Wert gibt. Die Funktion markAsTouched ruft onTouched auf und setzt die Variable touched auf true, falls das custom Formularfeld vorher noch nicht angeklickt wurde. Zum Schluss werden noch die beiden Methoden registerOnChange und registerOnTouched benötigt. Beide updaten eigentlich nur ihren jeweiligen Wert. Die Funktion registerOnChange updated den Wert onChange und registerOnTouched updated den Wert onTouched. Das reactive Form Module kann auch ganze Formulare disablen. Damit auch das custom reactive Formfeld darauf hört, wird die Methode setDisabledState genutzt. Mit dieser Methode kann die Variable isDisabled verändert werden.
writeValue(value: string): void { this.value = value; } markAsTouched(): void { if (!this.touched) { this.onTouched(); this.touched = true; } } registerOnChange(onChange: any): void { this.onChange = onChange; } registerOnTouched(onTouched: any): void { this.onTouched = onTouched; } setDisabledState(isDisabled: boolean): void { this.isDisabled = isDisabled; }
SVG Gruppen mit den Formularvariablen verknüpfen
Damit der SVG Büroplan auch funktional nutzbar ist brauchen wir noch die Möglichkeit einzelne Arbeitsplätze auszuwählen. Hierfür nutzen wir die Angular Bindingmöglichkeiten. Eine Funktion isSelected() wird benötigt, um das Sytling des Arbeitsplatzes zu erkenn bzw. zu verändern.
[class.selected]="isSelected('seat-0')" (click)="select('seat-0')"
Außerdem eine Funktion select(), welche den ausgewählten Platz anhand eines Namens ( „seat-1“, „seat-2“…) erkennt. Innerhalb dieser select() Funktion wird zuerst überprüft, ob das Formular allgemein disabled ist. Wenn das nicht der Fall ist überprüft eine zweite Abfrage, ob der Platz bereits ausgewählt wurde. Falls das der Fall ist wird der value zu nichts, ansonsten wird der value zu dem Namen des Arbeitsplatzes, also beispielsweise „seat-3“.
public select(option: string): void { if (!this.isDisabled) { if (this.isSelected(option)) { this.value = ''; } else { this.value = option; } this.onChange(this.value); this.markAsTouched(); } } public isSelected(option: string): boolean { return option === this.value; }
Ein Gedanke zu “Wie man eine Reactive Angular Form SVG mit klickbaren Elementen erstellt”