GEDOPLAN
Webprogrammierung

Angular – eigene ReactiveForm Komponenten

Webprogrammierung
cake 1587096 640

In größeren Projekten werden wir relativ schnell den Punkt erreichen in dem sich der ein oder andere Entwickler denkt: „Moment das hatten wir doch schon mal“. Wenn noch nicht geschehen ist das der Zeitpunkt um für den so identifizierten Bereich / Funktion eine eigene Komponente aus zu bilden. Wenn es sich dabei um ein Form-Element handelt mag es zuerst einmal nicht so offensichtlich sein wie man das am besten bewerkstelligt…

cake 1587096 640

Nehmen wir ein einfaches konkretes Beispiel: in unserer Anwendung haben wir eine Auswahlbox von Benutzern durch die z.B. eine Aufgabe, ein Projekt etc. zugeordnet werden soll. Diese Auswahl von Benutzern wird nun an verschiedenen Stellen der Anwendung benötigt. Also entwickeln wir eine eigenen Komponente, was dank Angular-CLI ja auch ein Kinderspiel ist:

ng generate component UserSelector

So einfach ist es in diesem Beispiel jedoch erst einmal nicht. Das Ziel soll es ja sein unsere Komponente als Teil eines Formulars zu verwenden und eine Deklaration analog zu den Standard-HTML Elementen zu erreichen:

<form [formGroup]="form">
  <label for="user">Benutzer: </label>
  <app-user-selector formControlName="userId" id="user"></app-user-selector>
  <label for="comment">Kommentar: </label>
  <input type="text" id="comment" formControlName="comment"/>
</form>

app.component.html > Verwendung unserer Komponente

wir wollen hier einen ReactiveForm-Ansatz verfolgen bei dem die Registrierung von Datenmodel und Validatoren in der Komponentenklasse geschieht, dazu wird hier der Name des Controls mittels ‚formControlName‘ eingesetzt. Die Alternative wäre der Einsatz von „ngModel“

In unserem Projekt setzen wir PrimeNG als Komponentenbibliothek ein, unsere UserSelector Komponente wird also lediglich eine Art Wrapper, hier das Template:

<span [formGroup]="form">
  <p-dropdown
    [options]="users"
    optionLabel="name"
    class="form-control"
    [autoDisplayFirst]="false"
    dataKey="id"
    formControlName="value"
  ></p-dropdown>
</span>

user-selector-component.html > Template unserer Komponente

wir verwenden hier eine PrimeNG Komponente ‚p-dropdown‘. Für unsere eigene Komponente spannend ist hier die Erzeugung einer eigenen FormGroup innerhalb unserer Komponente, das ermöglicht uns die PrimeNG Komponente innerhalb „ganz normal“ mit unserer Komponenten-Klasse zu verbinden

import { Component, OnInit, forwardRef } from '@angular/core';
import { FormGroup, ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder } from '@angular/forms';
import { DemoService } from '../demo.service';

// 1
@Component({
  selector: 'app-user-selector',
  templateUrl: './user-selector.component.html',
  styleUrls: ['./user-selector.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => UserSelectorComponent),
      multi: true
    }
  ]
})
//2
export class UserSelectorComponent implements ControlValueAccessor {
  users: any[];

  form: FormGroup;

  //3
  constructor(private service: DemoService, builder: FormBuilder) {
    service.getAll().subscribe(r => (this.users = r));

    this.form = builder.group({
      value: ['']
    });
    //4
    this.form.controls.value.valueChanges.subscribe(c => {
      this.onChange(c.id);
    });
  }

  //5
  writeValue(obj: number): void {
    if (obj) {
      const value = this.users.find(e => e.id == obj);
      if (value) this.form.patchValue({ value });
    }
  }

  //6
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  onChange: any = () => {};
  onTouched: any = () => {};
}

user-selector-component.ts > Komponentenklasse

1. die Metadaten für unsere Komponente. Neu an dieser Stelle ist die Registrierung unserer Komponente als Value-Accessor über das Provider Array, sehr einfach ausgedrückt ist dieses Deklaration bei jeder dieser Komponenten technisch bedingt genau so zu deklarieren ( sehr spannende Angular-Hintergrund-Themen zu dieser Deklaration: ‚Multi Providers‘ und ‚forwardRef‘ )

2. wir Implementieren das Interface ControlValueAccessor

3. Konstruktor. Neben einem Demo-Service ( der uns die Daten für unsere Liste ermittelt), lassen wir uns auch den FormBuilder von Anuglar injizieren um die FormGroup auf zu bauen

4. hier wird es spannender: wir registrieren einen Change-Listener an unserer internen FormControl ( die PrimeNG Dropdown-Liste ). Wenn der User hier eine Änderungen vornimmt wollen wir dieses Event „weiter geben“ und den Verwender unserer Komponente informieren, dazu wird die Methode onChange ( Interface ) verwendet. ( In unserem Beispiel nehmen wir hier auch eine Umwandlung vor: unserer DropDown-Liste liefert ein komplexes User-Objekt, in unserer Anwendung, also in den anderen fachlichen Objekten verwenden wir lediglich die Id.

5. writeValue ( Interface ) ist die Methode welche aufgerufen wir um ein Objekt welches aus dem Model kommt in der UI darzustellen, also beim Rendern oder bei Änderungen der Form. Wir bekommen an dieser Stelle die Id des bereits selektierten Benutzers, anhand dessen wir das komplexe User-Objekt aus unserem Array ermitteln und unser internes Formular damit aktualisieren.

6. Interface Methoden um die Registrierungen von EventListenern zu ermöglichen

Das war’s damit haben wir eine Komponente welche ganz einfach in unseren Formularen eingesetzt werden kann. Auf diese Weise lassen sich natürlich auch gänzlich eigene Komponenten ( ohne PrimeNG ) implementieren oder wesentlich komplexere wiederverwendbare Komponenten.

Noch mal gucken? > GITHUB

Schreibe einen Kommentar

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

Bitte füllen Sie dieses Feld aus.
Bitte füllen Sie dieses Feld aus.
Bitte gib eine gültige E-Mail-Adresse ein.
Sie müssen den Bedingungen zustimmen, um fortzufahren.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Kurse

weitere Blogbeiträge

IT-Training - GEDOPLAN
Jakarta EE (Java EE)

Jackson, mehr Annotationen

In früheren Artikeln haben wir bereits einige Features von Jackson gesehen die uns das Erzeugen und Verarbeiten von JSON-Strukturen einfacher…

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!