Angular Contact Form with Reactive Forms and HttpClient
Build a contact form in Angular using ReactiveFormsModule and HttpClient. Includes validation, loading states, error handling, and submission to Formboost — no backend required.
Angular Contact Form with Reactive Forms and HttpClient
Angular's ReactiveFormsModule and HttpClient are the right tools for building robust contact forms. This guide shows how to build a complete contact form component in Angular — with validation, loading state, error handling, and submission to an external endpoint — no backend server required.
Prerequisites
- Angular v15+ project
ReactiveFormsModuleandHttpClientModuleimported
Setup
Add HttpClientModule to your app module (or use provideHttpClient() in standalone apps):
1// app.module.ts
2import { HttpClientModule } from '@angular/common/http';
3
4@NgModule({
5 imports: [
6 BrowserModule,
7 ReactiveFormsModule,
8 HttpClientModule,
9 ],
10})
11export class AppModule {}For standalone components (Angular 17+):
1// main.ts
2import { provideHttpClient } from '@angular/common/http';
3
4bootstrapApplication(AppComponent, {
5 providers: [provideHttpClient()],
6});The Contact Form Component
1// contact-form.component.ts
2import { Component } from '@angular/core';
3import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
4import { HttpClient } from '@angular/common/http';
5import { CommonModule } from '@angular/common';
6
7@Component({
8 selector: 'app-contact-form',
9 standalone: true,
10 imports: [ReactiveFormsModule, CommonModule],
11 template: `
12 <div *ngIf="submitted; else formTemplate">
13 <p>Thanks! We'll be in touch soon.</p>
14 </div>
15
16 <ng-template #formTemplate>
17 <form [formGroup]="form" (ngSubmit)="onSubmit()">
18 <div>
19 <label for="name">Name</label>
20 <input id="name" type="text" formControlName="name" />
21 <span *ngIf="f['name'].touched && f['name'].errors?.['required']">
22 Name is required
23 </span>
24 </div>
25
26 <div>
27 <label for="email">Email</label>
28 <input id="email" type="email" formControlName="email" />
29 <span *ngIf="f['email'].touched && f['email'].errors?.['required']">
30 Email is required
31 </span>
32 <span *ngIf="f['email'].touched && f['email'].errors?.['email']">
33 Enter a valid email address
34 </span>
35 </div>
36
37 <div>
38 <label for="message">Message</label>
39 <textarea id="message" formControlName="message" rows="5"></textarea>
40 </div>
41
42 <p *ngIf="error" style="color: red">{{ error }}</p>
43
44 <button type="submit" [disabled]="submitting || form.invalid">
45 {{ submitting ? 'Sending…' : 'Send Message' }}
46 </button>
47 </form>
48 </ng-template>
49 `,
50})
51export class ContactFormComponent {
52 form: FormGroup;
53 submitting = false;
54 submitted = false;
55 error = '';
56
57 constructor(private fb: FormBuilder, private http: HttpClient) {
58 this.form = this.fb.group({
59 name: ['', Validators.required],
60 email: ['', [Validators.required, Validators.email]],
61 message: [''],
62 });
63 }
64
65 get f() {
66 return this.form.controls;
67 }
68
69 onSubmit() {
70 if (this.form.invalid) return;
71
72 this.submitting = true;
73 this.error = '';
74
75 this.http
76 .post('https://formboost.app/f/YOUR_ENDPOINT_ID', this.form.value)
77 .subscribe({
78 next: () => {
79 this.submitted = true;
80 this.submitting = false;
81 },
82 error: () => {
83 this.error = 'Something went wrong. Please try again.';
84 this.submitting = false;
85 },
86 });
87 }
88}Replace YOUR_ENDPOINT_ID with your Formboost endpoint ID.
Template-Driven Alternative
If you prefer template-driven forms:
1<!-- contact-form.component.html -->
2<form #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm)">
3 <div>
4 <label for="name">Name</label>
5 <input id="name" name="name" ngModel required #name="ngModel" />
6 <span *ngIf="name.touched && name.invalid">Name is required</span>
7 </div>
8
9 <div>
10 <label for="email">Email</label>
11 <input id="email" name="email" type="email" ngModel required email #email="ngModel" />
12 <span *ngIf="email.touched && email.errors?.['required']">Email is required</span>
13 <span *ngIf="email.touched && email.errors?.['email']">Invalid email</span>
14 </div>
15
16 <div>
17 <label for="message">Message</label>
18 <textarea id="message" name="message" ngModel rows="5"></textarea>
19 </div>
20
21 <button type="submit" [disabled]="contactForm.invalid">Send</button>
22</form>1onSubmit(form: NgForm) {
2 this.http
3 .post('https://formboost.app/f/YOUR_ENDPOINT_ID', form.value)
4 .subscribe({
5 next: () => { this.submitted = true; },
6 error: () => { this.error = 'Something went wrong.'; },
7 });
8}Validation Summary
| Validator | Usage |
|---|---|
Validators.required | Field must not be empty |
Validators.email | Must be a valid email format |
Validators.minLength(n) | Minimum character count |
Validators.maxLength(n) | Maximum character count |
Validators.pattern(regex) | Custom regex validation |
Getting Your Endpoint
- Sign up at dashboard.formboost.app — free, no credit card
- Create a new endpoint and copy the URL
- Replace
YOUR_ENDPOINT_IDin the component above