#angular #angular-reactive-forms #angular-template-form
#angular #угловые реактивные формы #angular-template-form
Вопрос:
Я работаю над Angular 10. Прошло 3 дня, как у меня возникла проблема с формами, управляемыми шаблонами angular, и реактивными формами.
Что я хочу сделать: создать сложную форму со многими дочерними компонентами (чтобы отделить много кода). Затем одним нажатием кнопки я хочу проверить все формы и дочерние компоненты. Я хочу как-то проверить дочерние компоненты.
Что я создал: форма, управляемая шаблоном, с 2-сторонними привязками, потому что я считаю, что это проще и не нужно для моей ситуации.
Мне удалось заставить проверку работать только для базовой формы. Я не смог сделать это на дочерних компонентах. Я видел много сообщений об этой проблеме и много пробовал, но я очень смущен.
Мой код :
mainForm.html
<div class="card m-3">
<div class="card-body">
<fieldset>
<form name="form" (ngSubmit)="f.form.valid amp;amp; onSubmit()" #f="ngForm" novalidate>
<div class="form-row">
<div class="form-group col">
<label>Title</label>
<select name="title" class="form-control" [(ngModel)]="model.title" #title="ngModel" [ngClass]="{ 'is-invalid': f.submitted amp;amp; title.invalid }" required>
<option value=""></option>
<option value="Mr">Mr</option>
<option value="Mrs">Mrs</option>
<option value="Miss">Miss</option>
<option value="Ms">Ms</option>
</select>
<div *ngIf="f.submitted amp;amp; title.invalid" class="invalid-feedback">
<div *ngIf="title.errors.required">Title is required</div>
</div>
</div>
<div class="form-group col-5">
<label>First Name</label>
<input type="text" name="firstName" class="form-control" [(ngModel)]="model.firstName" #firstName="ngModel" [ngClass]="{ 'is-invalid': f.submitted amp;amp; firstName.invalid }" required>
<div *ngIf="f.submitted amp;amp; firstName.invalid" class="invalid-feedback">
<div *ngIf="firstName.errors.required">First Name is required</div>
</div>
</div>
<div class="form-group col-5">
<label>Last Name</label>
<input type="text" name="lastName" class="form-control" [(ngModel)]="model.lastName" #lastName="ngModel" [ngClass]="{ 'is-invalid': f.submitted amp;amp; lastName.invalid }" required>
<div *ngIf="f.submitted amp;amp; lastName.invalid" class="invalid-feedback">
<div *ngIf="lastName.errors.required">Last Name is required</div>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col">
<label>Date of Birth</label>
<input type="date" name="dob" class="form-control" [(ngModel)]="model.dob" #dob="ngModel" [ngClass]="{ 'is-invalid': f.submitted amp;amp; dob.invalid }" required pattern="^d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$">
<div *ngIf="f.submitted amp;amp; dob.invalid" class="invalid-feedback">
<div *ngIf="dob.errors.required">Date of Birth is required</div>
<div *ngIf="dob.errors.pattern">Date of Birth must be a valid date in the format YYYY-MM-DD</div>
</div>
</div>
</div>
> One child component for demonstration(there are more)--code continues below
<app-extra-info [(model)]="model"></app-extra-info> pass model in children
<div class="text-center">
<button class="btn btn-primary mr-1">Register</button>
</div>
</form>
</fieldset>
</div>
</div>
childcomponent.html:
<div class="form-group row">
<label class="col-2 col-form-label">Gender</label>
<div class="col-4">
<select id="gender" name="gender" class="form-control" required>
<option>Male</option>
<option>Female</option>
<option>Not Specify</option>
<option></option>
</select>
</div>
</div>
<div class="form-group row">
<label>Pet name</label>
<input type="text" name="petName" class="form-control" [(ngModel)]="model.petName" #petName="ngModel" [ngClass]="{ 'is-invalid': petName.invalid amp;amp; petName.dirty}" required>
<div *ngIf="petName.invalid" class="invalid-feedback">
<div *ngIf="petName.errors.required">Pet Name is required</div>
</div>
</div>
childrencomponent.ts:
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-extra-info',
templateUrl: './extra-info.component.html',
styleUrls: ['./extra-info.component.css']
})
export class ExtraInfoComponent implements OnInit {
@Input() model: any;
constructor() { }
ngOnInit(): void {
}
}
parent.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-user-settings-form',
templateUrl: './user-settings-form.component.html',
styleUrls: ['./user-settings-form.component.css']
})
export class UserSettingsFormComponent implements OnInit {
model: any = {};
constructor() {
}
ngOnInit() {
}
onSubmit() {
alert(JSON.stringify(this.model, null, 4));
}
}
Код работает как есть. Проблема в том, что когда я нажимаю Register , ничего в дочернем компоненте не проверяется.(в данном случае имя питомца и пол)
Демонстрационное изображение: Форма
Вопросы, которые у меня есть:
-
Нужно ли мне использовать шаблонные или реактивные формы? Я уже говорил, что формы, основанные на шаблонах, проще и не нужны в моей ситуации, но я не совсем уверен в этом.
-
Нужно ли мне передавать форму дочерним элементам или передавать дочерние элементы в основную форму? Есть ли способ реализовать проверку дочерних компонентов с использованием формы, управляемой шаблоном?
Пожалуйста, может кто-нибудь просветить меня, как правильно поступить, дать мне отзыв и любое предложение. Не стесняйтесь спрашивать меня о чем-либо или если вы хотите получить более подробную информацию.
Спасибо за ваше время.
Ответ №1:
Предыдущий ответ, на мой взгляд, является хакерским ответом, который создает дополнительную нагрузку на ваш код typescript и обходит механизмы фреймворка. Это особенно излишне, когда вы используете формы на основе шаблонов, которые не требуют кода typescript для управления формой.
Решение? Используйте то, что предоставляет фреймворк (внутри вашего дочернего @Component({})):
viewProviders: [{provide: ControlContainer, useExisting: NgForm}]
Рабочий пример этого см. в этом репозитории
Ответ №2:
Вы можете передать в formControl
:
@Input() formControl: FormControl;
Затем передайте это следующим образом
<app-extra-info [formControl]="f.get('petName')"></app-extra-info> pass model in children
затем передайте форму отправителю
<form name="form" (ngSubmit)="onSubmit(f.form)" #f="ngForm" novalidate>
onSubmit(form: FormGroup) {
form.get('petName').updateValueAndValidity();
alert( form.get('petName').valid);
}
Комментарии:
1. Я немного смущен, FormControl не используется для реактивной формы? Или каково использование FormControl в форме, управляемой шаблоном?
Ответ №3:
Мне удалось реализовать то, что работает.
Что я сделал, так это добавил @ViewChild(ChildComponent) child-Component: ChildComponent;
в parent.ts.
Затем в дочернем компоненте я создал функцию, которая проверяет дочернюю форму, и, если есть какие-либо ошибки, я возвращаю логическую переменную. Эта функция используется в родительском. Я делаю это для каждого дочернего элемента. Вкратце, я выполняю проверку формы в каждом дочернем компоненте и получаю логическую переменную в родительском.
В родительском у меня есть что-то вроде :
if (!this.child-Component.validform) {
console.log("child has missing inputs");
}