Реактивная форма с использованием ControlValueAccessor для SubForm Показать ошибки при отправке - PullRequest
1 голос
/ 30 сентября 2019

У меня есть простая страница входа с реактивной формой и компонентом подчиненной формы app-login-form, которая использует ControlValueAccessor, которая работает, но я не могу понять, как отобразить ошибки в подчиненной форме. Это пример, прежде чем я начну создавать более сложные формы.

При отправке я пытаюсь получить доступ к подчиненной форме и markAllAsTouched, но когда я наблюдаю за элементами, классы не меняются.

Я сделал быстрый StackBlitz , чтобы показать, что я делаю. Как получить сообщения об ошибках при отправке формы?

public onSubmit(event: Event): void {
  if (this.form.valid) {
    console.log('VALID', this.form.value);
  } else {
    console.log('INVALID', this.form.value);

    Object.keys(this.form.controls).forEach((controlName) => {
      console.log('SHOW_ERRORS', controlName);
      const control = this.form.get(controlName);
      // ISSUE: Nothing changes on the element still ng-untouched, and 
      // was expecting it to be ng-touched
      control.markAllAsTouched();
    });
  }
}

Ответы [ 3 ]

0 голосов
/ 30 сентября 2019

проблема в том, что из родительского элемента у вас есть только элемент управления, когда помечается как коснувшаяся отметка как прикосновенная к этому элементу управления, но не элементы login.component. это вы помечаете как затронутые «логин», но НЕ «имя пользователя» и «пароль». И нет, логин не имеет представления о входах 'username' и 'password'

Одним из решений может быть:

Вы можете добавить в свой loginForm.component функцию:

public markAllAsTouched()
{
    this.form.markAllAsTouched();
}

Затем используйте ссылку на переменную или ViewChild в вашем приложении. Main

@ViewChild(LoginFormComponent,{static:false}) loginForm:LoginFormComponent
//in submit simple
 public onSubmit(event: Event): void {
    if (this.form.valid) {
      console.log('VALID', this.form.value);
    } else {
      console.log('INVALID', this.form.value);
      this.loginForm.markAllAsTouched();
    }
  }

со ссылкой на шаблон, простое использование в .html

<form (ngSubmit)="onSubmit(loginForm)">
  <app-login-form #loginForm formControlName="login"></app-login-form>
</form>

 //and in submit
 public onSubmit(loginForm:any): void {
    loginForm.markAllAsTouched()
 }

Другое решение - создать пользовательскийErrorStateMatcher, см. документы . Угловая материальная ошибка показывается дефектом, если элемент управления затронут и недействителен, но вы можете изменить это поведение, чтобы показывать его, если он недействителен, а «другая вещь» затронута. (это еще одна вещь - это ngControl, который вы вводите) В этом SO вопросе у вас есть пример customErrorMatcher, который показывает ошибку, когда formControl имеет ошибки (это более сложно, но это способы сделать пользовательский formControlкоторые используют внутренне угловые материальные входы)

0 голосов
/ 30 сентября 2019

Я бы выбрал немного другой подход и не использовал бы ControlValueAccessor, а вместо этого "обычный" дочерний компонент и использовал бы ControlContainer, тогда вы можете пропустить все эти markAsTouched вещи, так как родитель будет знать о происходящемon in child.

parent:

this.form = this.formBuilder.group({});

template:

<app-login-form></app-login-form>

Дочерний компонент, в который мы добавляем formcontrols к существующей родительской форме:

@Component({
  selector: "app-login-form",
  templateUrl: "./login-form.component.html",
  styleUrls: ["./login-form.component.css"],
  viewProviders: [
    {
      provide: ControlContainer,
      useExisting: FormGroupDirective
    }
  ]
})
export class LoginFormComponent implements OnInit {

  childForm: FormGroupDirective;
  constructor(
    parentForm: FormGroupDirective,
    private fb: FormBuilder
    ) {
    this.childForm = parentForm;
  }

  ngOnInit() {
    this.childForm.form.addControl('username', this.fb.control('', [Validators.required]));
    this.childForm.form.addControl('password', this.fb.control('', [Validators.required]));
  }
}

Тогда в шаблоне вы просто используете formControlName вместо [formControl], например:

 <input matInput formControlName="username">
 <mat-error *ngIf="childForm.hasError('required', 'username')">Required</mat-error>

Также удалите теги формы у дочернего элемента, а также не забудьте добавить type="button" в иконку, в противном случаеКнопка будет считаться submit.

Из родительской формы отправки вы можете удалить: control.markAllAsTouched();

Я бы разбудил ваш стек с полным кодом, но похоже, что я 'Мне не разрешено его раскошелиться. Надеюсь, я не забуду упомянуть все внесенные мной изменения, в противном случае, пожалуйста, предоставьте стек, который можно разветвить.

0 голосов
/ 30 сентября 2019

Можете ли вы попытаться вызвать updateValueAndValidity за markAllAsTouched

control.markAllAsTouched();
control.updateValueAndValidity();

Или вы можете запустить рекурсивную проверку с кодом на Угловая форма updateValueAndValidity всех дочерних элементов управления

private triggerValidation(control: AbstractControl) {
    if (control instanceof FormGroup) {
        const group = (control as FormGroup);

        for (const field in group.controls) {
            const c = group.controls[field];

            this.triggerValidation(c);
        }
    }
    else if (control instanceof FormArray) {
        const group = (control as FormArray);

        for (const field in group.controls) {
            const c = group.controls[field];

            this.triggerValidation(c);
        }
    }

    control.updateValueAndValidity({ onlySelf: false });
}
...