
Angular 5: Typed Reactive Forms
/ 2 min read
Table of Contents
Users can read and update existing topics. Now we will let them create one with a validated form.
Reactive form mental model
The TypeScript class owns the form model and validation. The HTML template connects inputs to that model and displays feedback.
1. Add the route
Generate the component:
ng generate component pages/topic-formPlace this route before topics/:id:
{ path: 'topics/new', loadComponent: () => import('./pages/topic-form/topic-form').then(module => module.TopicForm)}Otherwise Angular may interpret the word new as an ID.
2. Build a typed form
Import ReactiveFormsModule in the component and create the form:
private readonly formBuilder = inject(FormBuilder);
protected readonly topicForm = this.formBuilder.nonNullable.group({ name: ['', [Validators.required, Validators.minLength(3)]], completed: [false]});nonNullable means controls produce string and boolean, not string | null and boolean | null.
3. Connect the template
<form [formGroup]="topicForm" (ngSubmit)="submit()"> <label for="name">Topic name</label> <input id="name" type="text" formControlName="name">
@if (topicForm.controls.name.touched) { @if (topicForm.controls.name.hasError('required')) { <p class="error">Topic name is required.</p> } }
<label> <input type="checkbox" formControlName="completed"> Already completed </label>
<button type="submit">Add topic</button></form>4. Add domain validation
private validateTopicName( control: AbstractControl): ValidationErrors | null { const name = String(control.value).trim().toLowerCase();
if (!name) return { whitespace: true };
const duplicate = this.topicService.topics().some( topic => topic.name.toLowerCase() === name );
return duplicate ? { duplicate: true } : null;}Add it to the name control with an explicitly typed parameter if strict TypeScript reports an implicit any.
5. Submit
submit(): void { if (this.topicForm.invalid) { this.topicForm.markAllAsTouched(); return; }
const topic = this.topicService.addTopic( this.topicForm.controls.name.value, this.topicForm.controls.completed.value );
this.router.navigate(['/topics', topic.id]);}Checkpoint
Try an empty value, two letters, spaces, a duplicate such as Signals, and a valid new topic. Only the valid value should create a topic.
Small challenge
Disable the submit button while the form is invalid, then consider whether hiding submission entirely gives better or worse feedback to a beginner.
Navigation: Previous: Signal state · Series overview · Next: SSR