Published: December 05 2022

Angular 14 - Dynamic Add/Edit Form that Supports Create and Update Mode

Tutorial built with Angular 14.2.12

Other versions available:

This is a quick post on how to build a form that supports both adding and editing data in Angular 14 with Reactive Forms.

Forms for creating and updating records can often contain a lot of the same code. If the code is similar enough it can make more sense to combine the functionality into a single dynamic form than to maintain two files that are almost identical.

The example code is from a Angular Auth + CRUD tutorial I posted recently which allows adding and editing user data after login. For the full tutorial including a live demo see Angular 14 - User Registration and Login Example & Tutorial.

 

Users Add/Edit Component Template

Path: /src/app/users/add-edit.component.html

The users add/edit component template contains a dynamic form that supports both adding and editing users. The form is in edit mode when there a user id property in the current route, otherwise it is in add mode.

In edit mode the form is pre-populated with user details fetched from the API and the password field is optional. The dynamic behaviour is implemented in the users add/edit component.

<h1>{{title}}</h1>
<form *ngIf="!loading" [formGroup]="form" (ngSubmit)="onSubmit()">
    <div class="row">
        <div class="mb-3 col">
            <label class="form-label">First Name</label>
            <input type="text" formControlName="firstName" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.firstName.errors }" />
            <div *ngIf="submitted && f.firstName.errors" class="invalid-feedback">
                <div *ngIf="f.firstName.errors.required">First Name is required</div>
            </div>
        </div>
        <div class="mb-3 col">
            <label class="form-label">Last Name</label>
            <input type="text" formControlName="lastName" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.lastName.errors }" />
            <div *ngIf="submitted && f.lastName.errors" class="invalid-feedback">
                <div *ngIf="f.lastName.errors.required">Last Name is required</div>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="mb-3 col">
            <label class="form-label">Username</label>
            <input type="text" formControlName="username" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.username.errors }" />
            <div *ngIf="submitted && f.username.errors" class="invalid-feedback">
                <div *ngIf="f.username.errors.required">Username is required</div>
            </div>
        </div>
        <div class="mb-3 col">
            <label class="form-label">
                Password
                <em *ngIf="id">(Leave blank to keep the same password)</em>
            </label>
            <input type="password" formControlName="password" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" />
            <div *ngIf="submitted && f.password.errors" class="invalid-feedback">
                <div *ngIf="f.password.errors.required">Password is required</div>
                <div *ngIf="f.password.errors.minlength">Password must be at least 6 characters</div>
            </div>
        </div>
    </div>
    <div class="mb-3">
        <button [disabled]="submitting" class="btn btn-primary">
            <span *ngIf="submitting" class="spinner-border spinner-border-sm me-1"></span>
            Save
        </button>
        <a routerLink="/users" class="btn btn-link">Cancel</a>
    </div>
</form>
<div *ngIf="loading" class="text-center m-5">
    <span class="spinner-border spinner-border-lg align-center"></span>
</div>
 

Users Add/Edit Component

Path: /src/app/users/add-edit.component.ts

The users add/edit component is used for both adding and editing users in the angular tutorial app, the component is in edit mode when there a user id route parameter, otherwise it is in add mode.

In add mode the password field is required and the form fields are empty by default. In edit mode the password field is optional and the form is pre-populated with the specified user details, which are fetched from the API with the account service. For more info on validation with Reactive Forms see Angular 14 - Reactive Forms Validation Example.

On submit a user is either created or updated by calling the account service, and on success you are redirected back to the users list page with a success message.

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { first } from 'rxjs/operators';

import { AccountService, AlertService } from '@app/_services';

@Component({ templateUrl: 'add-edit.component.html' })
export class AddEditComponent implements OnInit {
    form!: FormGroup;
    id?: string;
    title!: string;
    loading = false;
    submitting = false;
    submitted = false;

    constructor(
        private formBuilder: FormBuilder,
        private route: ActivatedRoute,
        private router: Router,
        private accountService: AccountService,
        private alertService: AlertService
    ) { }

    ngOnInit() {
        this.id = this.route.snapshot.params['id'];

        // form with validation rules
        this.form = this.formBuilder.group({
            firstName: ['', Validators.required],
            lastName: ['', Validators.required],
            username: ['', Validators.required],
            // password only required in add mode
            password: ['', [Validators.minLength(6), ...(!this.id ? [Validators.required] : [])]]
        });

        this.title = 'Add User';
        if (this.id) {
            // edit mode
            this.title = 'Edit User';
            this.loading = true;
            this.accountService.getById(this.id)
                .pipe(first())
                .subscribe(x => {
                    this.form.patchValue(x);
                    this.loading = false;
                });
        }
    }

    // convenience getter for easy access to form fields
    get f() { return this.form.controls; }

    onSubmit() {
        this.submitted = true;

        // reset alerts on submit
        this.alertService.clear();

        // stop here if form is invalid
        if (this.form.invalid) {
            return;
        }

        this.submitting = true;
        this.saveUser()
            .pipe(first())
            .subscribe({
                next: () => {
                    this.alertService.success('User saved', { keepAfterRouteChange: true });
                    this.router.navigateByUrl('/users');
                },
                error: error => {
                    this.alertService.error(error);
                    this.submitting = false;
                }
            })
    }

    private saveUser() {
        // create or update user based on id param
        return this.id
            ? this.accountService.update(this.id!, this.form.value)
            : this.accountService.register(this.form.value);
    }
}
 

Angular Users Routing Module

Path: /src/app/users/users-routing.module.ts

The users routing module defines the routes for the /users section of the Angular app. It includes routes for listing, adding and editing users, and a parent route for the LayoutComponent which contains the common layout code for all pages in the users section.

The add and edit routes have different paths ('add' & 'edit/:id') but both load the same component (AddEditComponent) which dynamically modifies its behaviour based on the route.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { LayoutComponent } from './layout.component';
import { ListComponent } from './list.component';
import { AddEditComponent } from './add-edit.component';

const routes: Routes = [
    {
        path: '', component: LayoutComponent,
        children: [
            { path: '', component: ListComponent },
            { path: 'add', component: AddEditComponent },
            { path: 'edit/:id', component: AddEditComponent }
        ]
    }
];

@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})
export class UsersRoutingModule { }

 


Need Some Angular 14 Help?

Search fiverr for freelance Angular 14 developers.


Follow me for updates

On Twitter or RSS.


When I'm not coding...

Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!


Comments


Supported by