web-dev-qa-db-fra.com

Styles de validation de formulaire angulaire et de bootstrap

Je suis assez nouveau avec Angular et j'essaye de créer un formulaire d'inscription en utilisant Angular et Bootstrap 4.

Le résultat souhaité est d'utiliser les styles de Bootstrap avec la validation de Angular . Plus précisément, lors de la validation du formulaire, Angular applique les styles (ng-valid, ng-invalid, etc.) à deux endroits différents: l'élément d'entrée et l'élément de formulaire.

Deux questions:

1) Comme Bootstrap utilise 'has-danger' et 'has-success' au lieu de 'ng- [in] valid', est-il possible de configurer angular pour utiliser ces styles au lieu du style par défaut. Actuellement, j'envisage d'étendre bootstrap en ajoutant les styles angulaires (avec @extend has-danger/success)

2) Angular applique le style aux éléments input et form alors que bootstrap l'attend sur l'élément form-group. Est-il possible d'avoir angulaire y mettre le style à la place de l'élément en entrée (ou les deux?)

J'utilise des formulaires réactifs et j'aimerais éviter des choses comme (non testé):

<form>
    <div class="form-group" [class.has-error]="!fg.get('username').valid" [class.has-success]="fg.get('username').valid">
        <label>Username</label>
        <input formControlName="username" type="text"/>
    </div>
</form>

Y a-t-il un moyen simple (pas trop verbeux) d'y parvenir?

17
Rémi PIOTAIX

Si vous utilisez SASS, vous pouvez effectuer les opérations suivantes sans avoir à réécrire tous les css.

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

Remarque: vous devez importer bootstrap dans le cadre de votre construction SASS au lieu de le référencer directement.

Si vous n’utilisez pas SASS, c’est joli à installer, voyez ici Options de SASS angulaires CLI

16
Oliver

Une autre option est cette 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;
    }
}

Il ajoute simplement la classe is-invalid à chaque champ, si le champ est touché ou invalide. Elle se comporte fondamentalement de la même manière que la solution SASS d’Oliver, mais s’entend bien sans SASS et pourrait également produire une sortie compilée plus petite.

5
yankee

La meilleure idée qui m'est venue en examinant la documentation angulaire est d'utiliser une directive . Mon implémentation fonctionne uniquement avec les formulaires réactifs et si l'élément que vous souhaitez appliquer le style contient le contrôle de formulaire est le cas). Devrait être étendu pour assurer la compatibilité avec select et 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)
    }
  }
}

Exemple:

Le composant:

@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]
    });
  }
}

Et son 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>
1
Rémi PIOTAIX