- 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.
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 theupdateUserForm
variable from the data source(which isupdate-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 theupdateUserForm
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’sonSubmit()
methodformControlName="username"
: theformControlName
directive binds the username input field to the form groupupdateUserForm
so that if it is invalid the form group as a whole will be invalid as well[disabled]="!updateUserForm.valid"
: thedisable
attribute binds theupdateUserForm
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. TheupdateUserForm
is the actualFormgroup
instance that will hold the state of the form along with the states of the form’s input fields - The
ngOnInit()
lifecycle hook instantiates theupdateUserForm
form group and adds the requiredValidator
to the username field - The
onSubmit()
method, triggered when the user clicks the Submit button, will call the userService service class’supdateUser()
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
unique-username-update-validator.directive.ts
file
Step 3: Creating the 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 theAsyncValidatorFn
interface while taking aUserService
service class instance as an argument. This service class’supdateUser()
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
Figure 2: Username field with a unique username
Figure 3: Username field with a non-unique username, which triggers our custom 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.