<form [formGroup]="loginForm" (ngSubmit)="login()">
<div>
<label for="username">username</label>
<input
type="text"
name="username"
id="username"
[formControl]="username">
<div [hidden]="username.valid || username.untouched">
<div>
The following problems have been found with the username:
</div>
<div [hidden]="!username.hasError('minlength')">
Username can not be shorter than 5 characters.
</div>
<div [hidden]="!username.hasError('required')">
Username is required.
</div>
</div>
</div>
<div >
<label for="password">password</label>
<input
type="password"
name="password"
id="password" [formControl]="password">
<div [hidden]="password.valid || password.untouched">
<div>
The following problems have been found with the password:
</div>
<div [hidden]="!password.hasError('required')">
The password is required.
</div>
</div>
</div>
<button type="submit" [disabled]="!loginForm.valid">Log In</button>
</form>
Note that we have added rather robust validation on both the fields and the form itself, using nothing more than built-in validators and some template logic.
We are using .valid and .untouched to determine if we need to show errors - while the field is required, there is no reason to tell the user that the value is wrong if the field hasn't been visited yet.
For built-in validation, we are calling .hasError() on the form element, and we are passing a string which represents the validator function we included. The error message only displays if this test returns true.