Reactive Angular Form SVG

Wie man eine Reactive Angular Form SVG mit klickbaren Elementen erstellt

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

SVG office plan with clickable elements Angular Form SVG

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:

SVG office plan place selected Angular Form SVG

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;
}

Dateien zum Herunterladen

Ein Gedanke zu “Wie man eine Reactive Angular Form SVG mit klickbaren Elementen erstellt

Schreibe einen Kommentar

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