Cześć po 2 tygodniowej przerwie! W dzisiejszym artykule VALIDATION SERVICE – usprawniamy wyświetlanie errorów. W poprzednich artykułach pokazałem jak wyświetlać błędy walidacji na widoku. Wykonywałem to w następujący sposób:
<span *ngIf="modelForm.controls['firstname'].dirty && modelForm.controls['firstname'].errors?.startsWith">
Must start with {{ modelForm.controls['firstname'].errors?.startsWith }}
</span>
<span *ngIf="modelForm.controls.positions.controls[i].dirty && modelForm.controls['positions'].controls[i].errors?.oneRequired">
At least one field is required
</span>
Jeśli powyższy kod nie jest dla Ciebie jasny, zalecam zapoznanie się z I częścią artykułu o Reactive Forms, a najlepiej przeczytanie wszystkich części dotyczących Reactive Forms.
A teraz wyobraź sobie, że masz 10 inputów, w tym różne typy errorów (Required, minLength, maxLength, pattern etc). Zaczyna pachnieć spaghetti code? O tak…
Zdecydowanie będzie lepiej, jeśli nasze powyższe dwa elementy span z errorami, wyglądałyby następująco:
<error-message [control]="modelForm.controls['lastname']"></error-message>
<error-message [control]="modelForm.controls.positions.controls[i]"></error-message>
Żadnych dirty, logiki, tekstu, nazwy errora, wyłącznie referencja do obiektu = profit.
Pokażę, jak przygotować serwis oraz komponent, który zwróci nam odpowiedni typ błędu. Po co nam to?
- wszystkie teksty errorów trzymamy w jednym obiekcie, w przypadku zmiany możemy za jednym ruchem zmienić teksty w całej aplikacji
- czystsza templatka
- szybsza walidacja formularzy, nie tracimy zbędnego czasu na rozpisywanie
Poniżej link do plunkera, który zawiera kod z dalszej części artykułu:
Powyższy live example, jest on kontynuacją poprzedniego plunkera dotyczącego custom validatorów. W tym artykule interesują nas wyłącznie pliki:
- app/validation.service.ts
- app/error-message.component.ts
- app/error-message.component.html
- app/model-driven-form.component.html
Czas do kodu!
Validation Serivce
Zaczniemy od przygotowania serwisu, który będzie zwracać nam odpowiedni error. Jest to klasa z jedną metodą statyczną, która przyjmuje dwa argumenty – klucz w obiekcie, który zwraca walidator, oraz opcjonalnie wartość walidatora.
(app/validation.service.ts)
export class ValidationService {
static getValidationMessage(validator : string, validatorValue? : any) {
const messages = {
'required': 'This field is required',
'oneRequired': 'At least one field is required',
'minlength': `Min length is ${validatorValue.requiredLength} characters`,
'startsWith': `This field must start with ${validatorValue}`,
'digits': 'This field must be a digit'
};
return messages[validator];
}
}
Istotne, aby nasze klucze w obiekcie messages, nazywały się dokładnie tak jak klucze w obiekcie zwracanym przez walidator. Do obiektu messages dodajemy wszystkie możliwe walidacje, których używamy. Walidatory digits, oneRequired, startsWith stworzyłem w poprzednim artykule.
Serwis gotowy. Czas na komponent, wyświetlający odpowiedni error na widoku.
Error Message Component
Nasz komponent jest mały i prosty. Składa się wyłącznie z inputa i jednego gettera. Input() będzie odbierał referencję do obiektu, który reprezentuje kontrolkę lub grupę w formularzu (FormControl lub FormGroup).
Natomiast getter zwróci nam odpowiedni error z pomocą serwisu.
(app/error-message.component.ts)
import { Component, Input } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { ValidationService } from './validation.service';
@Component({
moduleId: module.id,
selector: 'error-message',
templateUrl: './error-message.component.html'
})
export class ErrorMessageComponent {
@Input() control: FormGroup | FormControl;
get errorMessage() {
for (let key in this.control.errors) {
if (this.control.errors.hasOwnProperty(key) && this.control.dirty) {
return ValidationService.getValidationMessage(key, this.control.errors[key]);
}
}
return null;
}
}
Jeszcze prosta templatka errora, którą oczywiście możesz ostylować wedle upodobań.
(app/error-message.component.html)
<div *ngIf="errorMessage !== null" class="error-container">
<span class="error-text">{{ errorMessage }}</span>
</div>
Oraz użycie na widoku:
(app/model-driven-form.component.html)
<input class="form-control" name="lastname" formControlName="lastname">
<error-message [control]="modelForm.controls['lastname']"></error-message>
Jak widzimy, w data-bound [control], przekazujemy wyłącznie referencję do danej kontrolki / grupy. Możesz również sparametryzować error-message komponent i np decydować czy dirty / touched / pristine występuje w danej walidacji.
Wyświetlanie errorów nigdy nie było przyjemniejsze!:-) Link do plunkera: