Угловая форма проверки и стили бутстрапа

Я совершенно новый с Angular, и я пытаюсь создать регистрационную форму, используя Angular и Bootstrap 4.

В результате я хотел бы использовать стили Bootstrap с валидацией Angular. Точнее, при проверке формы Angular применяет стили (ng- допустимо, ng- недействительно и т.д.) В двух разных местах: элемент ввода и элемент формы.

Два вопроса:

1) Поскольку Bootstrap использует "has-danger" и "has-success" вместо "ng- [in] valid", можно настроить угловое использование этих стилей вместо стандартного. В настоящее время я рассматриваю возможность расширения бутстрапа путем добавления угловых стилей (с @extend имеет-опасность/успех)

2) Угловая применяет стиль к элементам ввода и формы, тогда как bootstrap ожидает его на элементе группы форм. Возможно ли иметь угловой стиль вместо этого вместо элемента ввода (или обоих?)

Я использую реактивные формы, и я бы хотел избежать таких вещей, как (не тестировалось):

<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>

Есть ли простой способ (не слишком многословный) достижения этого?

Ответ 1

Если вы используете SASS, вы можете сделать следующее, не переписывая все css.

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

Примечание. Вам нужно будет импортировать bootstrap как часть вашей сборки SASS вместо ссылки непосредственно.

Если вы не используете SASS, это довольно удобно для установки, см. Здесь Параметры Angular CLI SASS

Ответ 2

Другим вариантом является эта директива:

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;
    }
}

Он просто добавляет is-invalid класс для каждого поля, если поле коснулось или недействительно. Он в основном ведет себя так же, как решение Oliver SASS, но обойдется без SASS и может также иметь меньший скомпилированный результат.

Ответ 3

Лучшая идея, которая пришла ко мне, глядя на угловые документы, - это использовать директиву. Моя реализация работает только с реактивными формами, и если элемент, который вы хотите применить к стилю, содержит элемент управления формой (который, если вы используете бутстрап, в этом случае). Должен быть расширен для совместимости с select и 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)
    }
  }
}

Пример:

Компонент:

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

И его шаблон:

<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>