Published on

Adding Unique Username Validators to Reactive Forms in Angular

Do you know the most important feature of building forms as a software developer? If you said validations then you are right! Form validations are very important for any type of form because they prevent malicious users from being able to damage our applications, they protect our users’ sensitive data, and they ensure that the form data is in the correct format when it is processed by our applications.

Username field with a unique username

Figure 1: Form Validations in action!

Prerequisites

Before starting this tutorial you will need the following:

  • Knowledge of Typescript(To learn more about Typescript visit typescriptlang.org/docs)
  • Basic understanding of how Angular works(To learn more about how Angular works read this article)
  • Basic understanding of web application architecture(visit this link for more info)

Then, in your Angular application you will need the following requirements

  • An Angular component that implements a Reactive form based on updating user data
  • A service class that can communicate with the backend server via HTTP requests

If you don’t have a component devoted to holding our reactive form, let’s create it now via:

ng generate component user-update-form

Step 1: Adding basic template code to our Update User form

Let’s start out by creating some base template code for our Update User Form component. Go to the user-update-form.component.html file and add the following code:

<form [formGroup]="updateUserForm" *ngIf="updateUserForm" (ngSubmit)="onSubmit()">
  <div class="">
    <div class="card-header bg-primary text-white">
      <h3>Update User</h3>
    </div>
    <div class="card-body">
      <div class="form-group row d-flex justify-content-center align-items-center">
          <label for="username" class="col-form-label col-sm-2 text-right">Username:</label>
          <div class="col-sm-5">
              <input type="text" class="form-control" name="username" formControlName="username"/>
          </div>
      </div>
    </div>
<div class="card-footer">
      <button [disabled]="!updateUserForm.valid" class="btn btn-primary mr-2" type="submit">Update</button>

    </div>
  </div>
</form>

Now here is an explanation for this html code:

  • [formGroup]="updateUserForm": this attribute one way binds the updateUserForm variable from the data source(which is update-user-form.component.ts file in this case) to the <form> HTML element.
  • *ngIf='updateUserForm': this directive is used hold off the rendering of the form until the updateUserForm variable is instantiated
  • (ngSubmit)='onSubmit()': this event binder is used to bind the form submission event, initiated by the user upon clicking the submit button, to the data source’s onSubmit() method
  • formControlName="username": the formControlName directive binds the username input field to the form group updateUserForm so that if it is invalid the form group as a whole will be invalid as well
  • [disabled]="!updateUserForm.valid": the disable attribute binds the updateUserForm to the submit button so that if the form is invalid, the submit button is disabled. This is an effective means of client-side form validation

Step 2: Adding basic Typescript code to our Update User Form

Now we can add some basic typescript code to our Update User Form component by using the user-update-form.component.ts file:

import { Component, OnInit } from '@angular/core';
import { UserService } from '../shared/user.service'
import { User } from '../shared/user.model';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';


export class UserUpdateFormComponent implements OnInit {
  selectedUser: User;
  updateUserForm: FormGroup;
constructor(
    private userService: UserService,
    private formBuilder: FormBuilder,
    ) {

    }
ngOnInit(){
    this.updateUserForm = this.formBuilder.group({
      _id: new FormControl(this.selectedUser._id),
      username: new FormControl(this.selectedUser.username, [
        Validators.required,
      ]
)
    });
  }

onSubmit(){
    this.userService.updateUser(this.updateUserForm.value).subscribe(user => {
      window.location.reload();
    },
      error => {
        console.log('Form Submit Error');
      });
  }

}

Here is an explanation for the important parts of our TypeScript code:

  • The selectedUser variable holds the user data in to be prepopulated on the Update User form. The updateUserForm is the actual Formgroup instance that will hold the state of the form along with the states of the form’s input fields
  • The ngOnInit() lifecycle hook instantiates the updateUserForm form group and adds the required Validator to the username field
  • The onSubmit() method, triggered when the user clicks the Submit button, will call the userService service class’s updateUser() method in order to upload the form’s data to the backend server via HTTP requests. After that, reload the page if the request was successful

Step 3: Creating the unique-username-update-validator.directive.ts file

Finally, we can create the unique username validator for our form. Let’s create a file called unique-username-update-validator.directive.ts in the src/app/shared directory in our Angular applications(if you don’t have a shared directory, I recommend creating one). Then we will add the following code to this file:

import { AbstractControl, AsyncValidatorFn } from '@angular/forms';
import { UserService } from './user.service';
import { map } from 'rxjs/operators';

export function uniqueUsernameUpdateValidator(userService: UserService): AsyncValidatorFn{
    return (c: AbstractControl): {[key: string]: boolean} | any => {
        var currentUsername = JSON.parse(sessionStorage.getItem("currentUser"))["username"];
        return userService.getUserByUsername(c.value)
            .pipe(
                map(user => {
                if(user.length > 0){
                    var username = user[0]["username"];
                    if(username != currentUsername){
                        return {'uniqueUsernameUpdate': true}
                    }else{
                        return null;
                    }
                }else{
                    return null;
                }
                })
            )
    }
}

Now I’ll explain the important parts of this file:

  • First we import the AsyncValidatorFn interface from the @angular/forms library, which is an interface that emits validations errors when they are present in the form
  • The uniqueUsernameUpdateValidator function implements the AsyncValidatorFn interface while taking a UserService service class instance as an argument. This service class’s updateUser() method is used for check with the backend server on whether or not the currently inputted username in a unique username or not. If the backend returns a username then we know that it’s a unique username error and therefore we return the JSON array {'uniqueUsernameUpdate'}: true
  • We have to take into account for the fact that the currently logged in user’s username cannot be recognized as an unique username error when it is typed in the username form input field. This is why the if(username != currentUsername) statement exists, as it prevents the validator from interpreting the currently logged in user’s username as a unique username validation error

Step 4: Adding our custom validator to Update User Form component’s form

Finally we have to add our newly created custom validator to our username FormControl class in the Update User Form component. To do that we need to import the custom validator into our component by adding the following import to our user-update-form.component.ts file:

import { uniqueUsernameUpdateValidator } from '../shared/unique-username-update-validator.directive';

Then we add our custom validator to our username FormControl class in the ngOnInit() method, like so:

ngOnInit(){
    this.updateUserForm = this.formBuilder.group({
    _id: new FormControl(this.selectedUser._id),
    username: new FormControl(this.selectedUser.username, [
    Validators.required,
    ],

    [uniqueUsernameUpdateValidator(this.userService)]
    )
  });
}

After this, we need to include the template code that will contain the error message for our unique username validator. Let’s add the following orange colored code to our user-update-form.component.html file:

<div class="card-body">
      <div class="form-group row d-flex justify-content-center align-items-center">
          <label for="username" class="col-form-label col-sm-2 text-right">Username:</label>
          <div class="col-sm-5">
              <input type="text" class="form-control" name="username" formControlName="username"/>

                <div class="alert alert-danger" *ngIf="username.invalid && (username.dirty || username.touched) && username.errors?.uniqueUsernameUpdate">
                Username is already taken! Please pick a different one...
              </div>
          </div>
      </div>
</div>

Step 5: Run application

Now for the moment of truth… Let’s run the following command to start our Angular application:

npm start
Username field with a unique username

Figure 2: Username field with a unique username

Username field with a non-unique username, which triggers our custom validator

Figure 3: Username field with a non-unique username, which triggers our custom validator

Username field being empty and touched triggers the required validator

Figure 4: Username field being empty and touched triggers the required validator

Conclusion

Well, that’s it for this tutorial! I truly hope I had the honor to teach you about creating unique username validators in Angular Reactive forms. If you have any questions or concerns please feel free to post a comment on this article and I will get back to you if I find the time.