Angular form validation and bootstrap styles Angular form validation and bootstrap styles angular angular

Angular form validation and bootstrap styles


If you're using SASS you can do the following with out needing to rewrite all the css.

.ng-touched.ng-invalid {  @extend .is-invalid;}

Note: you'll need to be importing bootstrap as part of your SASS build instead of reference it directly.

If you're not using SASS it's pretty to install see here Angular CLI SASS options


Another option is this directive:

import {Directive, HostBinding, Self} from '@angular/core';import {NgControl} from '@angular/forms';@Directive({    selector: '[formControlName],[ngModel],[formControl]',})export class BootstrapValidationCssDirective {    constructor(@Self() private cd: NgControl) {}    @HostBinding('class.is-invalid')    get isInvalid(): boolean {        const control = this.cd.control;        return control ? control.invalid && control.touched : false;    }}

It simply adds the is-invalid class to each field, if the field is touched or invalid. It basically behaves the same as Oliver's SASS-solution, but get's along without SASS and might also have a smaller compiled output.


The best idea that came to me while looking at the angular docs is to use a directive.My implementation works only with Reactive forms and if the element you want to apply the style contains the form control (which, if you use bootstrap is the case). Should be extended for compatibility with select and textarea.

import { Directive, ElementRef, Input, OnInit } from '@angular/core';import { FormControl, FormGroup } from '@angular/forms'@Directive({ selector: '[formValidationStyle]' })export class FormValidationStyleDirective implements OnInit {  @Input('formValidationStyle') private formGroup: FormGroup;  private component: FormControl;  static VALID_STYLE: string = 'has-success';  static INVALID_STYLE: string = 'has-danger';  constructor(private el: ElementRef) { }  ngOnInit(): void {    let componentName: string;    let inputElement = this.el.nativeElement.querySelector('input');    if (inputElement) {      componentName = inputElement.getAttribute('formControlName');    }    if (!componentName) {      console.error('FormValidationStyleDirective: Unable to get the control name. Is the formControlName attribute set correctly?')      return;    }    let control = this.formGroup.get(componentName)    if (!(control instanceof FormControl)) {      console.error(`FormValidationStyleDirective: Unable to get the FormControl from the form and the control name: ${componentName}.`)      return;    }    this.component = control as FormControl;    this.component.statusChanges.subscribe((status) => {      this.onStatusChange(status);    });    this.onStatusChange(this.component.status);  }  onStatusChange(status: string): void {    let cl = this.el.nativeElement.classList;    if (status == 'VALID') {      cl.add(FormValidationStyleDirective.VALID_STYLE)      cl.remove(FormValidationStyleDirective.INVALID_STYLE)    } else if (status == 'INVALID') {      cl.add(FormValidationStyleDirective.INVALID_STYLE)      cl.remove(FormValidationStyleDirective.VALID_STYLE)    }  }}

Example:

The component:

@Component({  selector: 'security-register',  templateUrl: './register.component.html'})export class RegisterComponent {  registerForm: FormGroup;  constructor(private http: Http, private fb: FormBuilder) {    this.registerForm = this.fb.group({       username: ['', Validators.required]    });  }}

And its template:

<form [formGroup]="registerForm" novalidate>  <div class="form-group" [formValidationStyle]="registerForm">    <label class="form-control-label" for="dbz-register-username">Login</label>    <input formControlName="username" type="text" class="form-control" id="dbz-register-username" required>  </div>  <div class="form-group">    <button type="submit" class="btn btn-primary">Register</button>  </div></form>