// app/custom-validation/customer-request.ts
export interface CustomerRequest {
id: number;
customerId: string;
date: Date;
message: string;
}
// app/custom-validation/not-in-year-validator.directive.ts
import { Directive, Input } from '@angular/core';
import {
AbstractControl,
NG_VALIDATORS,
ValidationErrors,
Validator,
ValidatorFn,
} from '@angular/forms';
export function notInYearValidator(notInYear: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!control.value) {
return null;
}
const date = control.value as Date;
return date.getFullYear() == notInYear
? { notInYear: { value: control.value } }
: null;
};
}
// IMPORTANT: The directive is not needed if you're using reactive forms
@Directive({
selector: '[appNotInYear]',
providers: [
{
provide: NG_VALIDATORS,
useExisting: NotInYearValidatorDirective,
multi: true,
},
],
})
export class NotInYearValidatorDirective implements Validator {
@Input('appNotInYear')
notInYear?: number;
validate(control: AbstractControl): ValidationErrors | null {
return this.notInYear ? notInYearValidator(this.notInYear)(control) : null;
}
}
// app/custom-validation/valid-customer-id-validator.directive.ts
import { Directive } from '@angular/core';
import {
AbstractControl,
NG_VALIDATORS,
ValidationErrors,
Validator,
ValidatorFn,
} from '@angular/forms';
export function validCustomerIdValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!control.value) {
return null;
}
const valid = /^[A-Z]{2}[0-9]{4}$/.test(control.value);
return valid ? null : { validCustomerId: { value: control.value } };
};
}
// IMPORTANT: The directive is not needed if you're using reactive forms
@Directive({
selector: '[appValidCustomerId]',
providers: [
{
provide: NG_VALIDATORS,
useExisting: ValidCustomerIdValidatorDirective,
multi: true,
},
],
})
export class ValidCustomerIdValidatorDirective implements Validator {
validate(control: AbstractControl): ValidationErrors | null {
return validCustomerIdValidator()(control);
}
}
<!-- app/custom-validation/customer-request-form-template/customer-request-form-template.component.html -->
<h1>Customer Request (template-based)</h1>
<mat-card>
<form (ngSubmit)="submit()" #customerRequestForm="ngForm">
<mat-form-field>
<mat-label>Customer ID</mat-label>
<input
matInput
required
appValidCustomerId
name="customerId"
[(ngModel)]="customerRequest.customerId"
#customerId="ngModel"
/>
<mat-error *ngIf="customerId.errors?.required"
>You must enter a value</mat-error
>
<mat-error *ngIf="customerId.errors?.validCustomerId"
>The ID must start with two uppercase letters followed by four
numbers</mat-error
>
</mat-form-field>
<mat-form-field>
<mat-label>Date</mat-label>
<input
matInput
required
[appNotInYear]="2020"
name="date"
[matDatepicker]="picker"
[(ngModel)]="customerRequest.date"
#date="ngModel"
/>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error *ngIf="date.errors?.required"
>You must enter a value</mat-error
>
<mat-error *ngIf="date.errors?.notInYear"
>The date cannot be in 2020</mat-error
>
</mat-form-field>
<mat-form-field>
<mat-label>Message</mat-label>
<textarea
matInput
required
name="message"
[(ngModel)]="customerRequest.message"
#message="ngModel"
></textarea>
<mat-error *ngIf="message.errors?.required"
>You must enter a value</mat-error
>
</mat-form-field>
<div class="form-buttons">
<button
mat-raised-button
type="submit"
color="primary"
[disabled]="loading || !customerRequestForm.form.valid"
>
Submit
</button>
</div>
</form>
</mat-card>
<h2>Customer Request (JSON)</h2>
<pre
>{{ customerRequest | json }}
</pre>
// app/custom-validation/customer-request-form-template/customer-request-form-template.component.ts
import { Component } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { finalize } from 'rxjs/operators';
import { CustomerRequest } from 'src/app/custom-validation/customer-request';
import { CustomerRequestService } from 'src/app/custom-validation/customer-request.service';
@Component({
selector: 'app-customer-request-form-template',
templateUrl: './customer-request-form-template.component.html',
})
export class CustomerRequestFormTemplateComponent {
readonly customerRequest: CustomerRequest = {
id: 0,
customerId: '',
date: new Date(),
message: '',
};
loading = false;
constructor(
private readonly customerRequestService: CustomerRequestService,
private readonly snackBar: MatSnackBar
) {}
submit(): void {
this.loading = true;
this.customerRequestService
.save(this.customerRequest)
.pipe(finalize(() => (this.loading = false)))
.subscribe(({ id }) => {
this.snackBar.open(`Customer request saved with ID #${id}`, 'Close');
});
}
}
// app/custom-validation/customer-request.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { CustomerRequest } from 'src/app/custom-validation/customer-request';
export interface CustomerRequestSaveResponse {
id: number;
}
@Injectable({
providedIn: 'root',
})
export class CustomerRequestService {
save(
customerRequest: CustomerRequest
): Observable<CustomerRequestSaveResponse> {
console.log('Save:', customerRequest);
return of({
id: 1,
}).pipe(delay(1000));
}
}