Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 & Bootstrap 4 - Consolidating Validation Classes

I discovered today that validation is not always being displayed correctly in my application. In one particular example, angular will fail for required if <option [ngValue]="null"> is selected; however, the browser sees <option value="0: null"> and does not fail on required.

I'd like to switch to using Bootstrap's server-side validation classes to manually control when validation is displayed: https://getbootstrap.com/docs/4.0/components/forms/#server-side

Angular has ng-valid and ng-invalid and Bootstrap has is-valid and is-invalid. I don't want to copy the bootstrap CSS and just change the names. Rather, it'd be nice if there was a way to detect the angular classes and automatically add the bootstrap equivalent, something like this:

<select #s="ngModel"
        id="s"
        name="s"
        [ngModel]="myModel"
        required
        class="form-control"
        [class.is-valid]="s.valid"
        [class.is-invalid]="s.invalid">
    <option [ngValue]="null">Choose a value</option>
    <option *ngFor="let item of items" [ngValue]="item.id">{{item.Name}}</option>
</select>

If there was a way to do this without needing to repeat those [class...] attributes on every input, that'd be ideal.

like image 833
Travis Parks Avatar asked Oct 18 '17 12:10

Travis Parks


1 Answers

I actually took a look at the Angular source code to see how they were universally applying ng-valid and ng-invalid. https://github.com/angular/angular/blob/4.4.5/packages/forms/src/directives/ng_control_status.ts#L38-L57

I ended up taking their code and creating my own directive that simply emits the bootstrap validation classes:

import { Directive, Self } from "@angular/core";
import { NgControl } from "@angular/forms";

const controlStatusHost = {
    "[class.is-valid]": "ngClassValid",
    "[class.is-invalid]": "ngClassInvalid"
};

@Directive({ selector: "[formControlName],[ngModel],[formControl]", host: controlStatusHost })
export class BSControlStatusDirective {
    public constructor(@Self() private control: NgControl) {
    }

    get ngClassValid(): boolean {
        if (this.control.control == null) {
            return false;
        }
        return this.control.control.valid;
    }

    get ngClassInvalid(): boolean {
        if (this.control.control == null) {
            return false;
        }
        return this.control.control.invalid;;
    }
}

It's the host property on the decorator that's really doing all of the magic. Pretty neat!

like image 163
Travis Parks Avatar answered Sep 29 '22 22:09

Travis Parks