我是棱角分明的新手。
我正在构建一个具有联系和订阅形式的应用程序,这在网站中很常见。这两种形式都有名字,姓氏和电子邮件字段。我正在为这两种形式复制html标记和验证。
所以我决定将公共字段提取到一个单独的组件,以避免重复,但我很难将值从子组件传递给父组件,并保持提交按钮禁用,以及子组件中的无效输入。
我目前的代码如下。
personalinfo.component.html
<div [formGroup]="personalInfoForm" novalidate>
<div class="firstName">
<mat-form-field>
<input matInput placeholder="First Name"
formControlName="firstName">
<mat-error [hidden]="personalInfoForm.hasError('required', 'firstName')">
Please provide first name
</mat-error>
</mat-form-field>
</div>
<div class="lastName">
<mat-form-field>
<input matInput placeholder="Last Name" formControlName="lastName">
<mat-error [hidden]="personalInfoForm.hasError('required', 'lastName')">
Please provide last name
</mat-error>
</mat-form-field>
</div>
<div class="email">
<mat-form-field>
<input matInput placeholder="Email" formControlName="email">
<mat-error *ngIf="personalInfoForm.hasError('required', 'email')">
Please provide an email
</mat-error>
<mat-error *ngIf="personalInfoForm.hasError('pattern', 'email') &&
!personalInfoForm.hasError('required', 'email')">
Invalid email
</mat-error>
</mat-form-field>
</div>
</div>
personalinfo.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-personalinfo',
templateUrl: './personalinfo.component.html',
styleUrls: ['./personalinfo.component.scss']
})
export class PersonalinfoComponent implements OnInit {
readonly EMAIL_REGEX = "[a-zA-Z0-9.-_]{1,}@[a-zA-Z0-9.-_]{1,}.[a-zA-Z]{2,}";
personalInfoForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createPersonalInfoForm();
}
createPersonalInfoForm() {
this.personalInfoForm = this.fb.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
email: ['', [Validators.required, Validators.pattern(this.EMAIL_REGEX)]],
});
}
ngOnInit() {
}
}
contact.component.html
<div class="form-contact" flexLayout="row" fxLayoutAlign="center">
<form [formGroup]="contactForm"
novalidate
(ngSubmit)="sendMessage(contactForm.value, contactForm.invalid)">
<app-personalinfo></app-personalinfo>
<div class="subject">
<mat-form-field>
<input matInput
placeholder="Subject"
formControlName="subject" >
<mat-error *ngIf="contactForm.hasError('required', 'subject')">
Please provide a subject
</mat-error>
<mat-error *ngIf ="contactForm.hasError('minlength', 'subject') &&
!contactForm.hasError('required', 'subject')">
Minimum 5 characters
</mat-error>
</mat-form-field>
</div>
<div class="message">
<mat-form-field>
<textarea matInput
placeholder="Message"
matTextareaAutosize
matAutosizeMinRows="5"
matAutosizeMaxRows="10"
formControlName="message">
</textarea>
<mat-error *ngIf ="contactForm.hasError('required', 'message')">
Please provide a message
</mat-error>
<mat-error *ngIf ="contactForm.hasError('minlength', 'message') &&
!contactForm.hasError('required', 'message')">
Minimum 10 characters
</mat-error>
</mat-form-field>
</div>
<button mat-button
color="primary"
type="submit"
[disabled]="contactForm.invalid">
Submit
</button>
</form>
</div>
contact.component.ts
import { Component, OnInit } from '@angular/core';
import { FormControl, Validators, FormBuilder, FormGroup } from '@angular/forms';
// interface with fields (firstName, lastName and etc)
import { User } from '../user';
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.scss']
})
export class ContactComponent implements OnInit {
contactForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createContactForm();
}
createContactForm() {
this.contactForm = this.fb.group({
subject: ['', [Validators.required, Validators.minLength(5)]],
message: ['', [Validators.required, Validators.minLength(10)]]
});
}
ngOnInit() {
}
sendMessage(user, isInvalid: boolean)
{
if(!isInvalid){
console.log(user);
console.log(user.firstName);
console.log(user.lastName);
console.log(user.email);
console.log(user.subject)
console.log(user.message);
}
}
}
我发现了几个问题,但事情并不清楚。任何帮助将受到高度赞赏。
只需为共享表单创建可重用的组件即可。
共享form.component.ts
在你的shared-form.component
中,你定义了你的共享FormControls
。
接下来你需要掌握你的组件中的FormGroupDirective。使用此指令,您可以在Submit
范围之外调用Reset
和[formGroup]
通过此设置,您可以使用SharedForm进行远程控制! :)
你的shared-form.component.ts
看起来像这样:
import { Component, ViewChild, Optional } from '@angular/core';
import { FormGroup, FormBuilder, FormGroupDirective, Validators, NgForm } from '@angular/forms';
@Component({
selector: 'app-shared-form',
templateUrl: './shared-form.component.html',
styles: []
})
export class SharedFormComponent {
@ViewChild(FormGroupDirective) fgd: FormGroupDirective;
public form: FormGroup;
get emailCtrl() {
return this.form.get('email');
}
constructor(private fb: FormBuilder,
@Optional() fgdParent: FormGroupDirective) {
this.createForm();
if (fgdParent) {
fgdParent.ngSubmit.subscribe(() => this.fgd.onSubmit(null));
}
}
createForm() {
this.form = this.fb.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
});
}
}
shared-form.component.html
<div [formGroup]="form" novalidate>
<div class="firstName">
<mat-form-field>
<input matInput placeholder="First Name"
formControlName="firstName">
<mat-error>
Please provide first name
</mat-error>
</mat-form-field>
</div>
<div class="lastName">
<mat-form-field>
<input matInput placeholder="Last Name" formControlName="lastName">
<mat-error [hidden]="form.hasError('required', 'lastName')">
Please provide last name
</mat-error>
</mat-form-field>
</div>
<div class="email">
<mat-form-field>
<input matInput placeholder="Email" formControlName="email">
<mat-error *ngIf="emailCtrl.hasError('required')">
Please provide an email
</mat-error>
<mat-error *ngIf="emailCtrl.hasError('email') && !emailCtrl.hasError('required')">
Invalid email
</mat-error>
</mat-form-field>
</div>
</div>
personalinfo.component.ts
你的PersonalinfoComponent
只不过是你的SharedForm
的包装
personalinfo.component.html
<mat-card>
<h3>Subscribe</h3>
<mat-card-content>
<app-shared-form #form></app-shared-form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" type="button" (click)="onSubmit()">Submit</button>
<button mat-raised-button type="button" (click)="form.fgd.resetForm()">Reset</button>
</mat-card-actions>
</mat-card>
personalinfo.component.ts
import { Component, ViewChild } from '@angular/core';
import { SharedFormComponent } from '../shared/shared-form/shared-form.component';
@Component({
selector: 'app-personalinfo',
templateUrl: './personalinfo.component.html',
styleUrls: ['./personalinfo.component.css']
})
export class PersonalinfoComponent {
@ViewChild(SharedFormComponent) formComp: SharedFormComponent;
onSubmit() {
this.formComp.fgd.onSubmit(null);
if (!this.formComp.form.valid) { return; }
console.log('Ready to make an ajax call: ', this.formComp.form.getRawValue());
}
}
如果表格无效,我不是禁用按钮的忠实粉丝。因为用户在尝试点击“提交”按钮时看不到任何验证错误。更好的用户体验是,如果用户点击提交按钮,表单将触发验证,用户可以看到表单有什么问题。这是在Angular Material FormField中完全实现的。
有几个例子说明如何触发从ParentComponent
到ChildComponent
的提交事件。当我们通过ChildComponent
-Decorator获取ViewChild
时,我们可以在onSubmit()
上调用FormGroupDirective
并且验证被触发。要重置表单,只需调用resetForm()
。
contact.component.ts
ContactComponent
的用例与PersonalinfoComponent
非常相似。
就视图呈现而言,我们可以将ChildForm
(SharedFormComponent)注入我们的ParentForm
(ContactForm)
ngAfterViewInit() {
this.contactForm.addControl('personalInfo', this.childForm.form);
}
目前,当且仅当子表格有效时,联系表格才有效。
但这里非常酷的东西又是Angular强大的DI。
如果我们在Application中大量使用SharedForm,我们可以在SharedFormComponent中注入父FormGroupDirective
作为可选依赖项。因此,每当SharedFormComponent被包装在父FormGroup
中时,它就会自动获取父FormGroupDirective
。现在我们可以将父事件的提交事件委托给孩子。
constructor(private fb: FormBuilder,
@Optional() fgdParent: FormGroupDirective) {
this.createForm();
if (fgdParent) {
fgdParent.ngSubmit.subscribe(() => this.fgd.onSubmit(null));
}
}
contact.component.ts
import { SharedFormComponent } from './../shared/shared-form/shared-form.component';
import { FormGroup, FormBuilder, Validators, NgForm } from '@angular/forms';
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.css']
})
export class ContactComponent implements AfterViewInit {
@ViewChild(SharedFormComponent) childForm: SharedFormComponent;
contactForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createContactForm();
}
createContactForm() {
this.contactForm = this.fb.group({
subject: ['', [Validators.required, Validators.minLength(5)]],
message: ['', [Validators.required, Validators.minLength(10)]]
});
}
ngAfterViewInit() {
this.contactForm.addControl('personalInfo', this.childForm.form);
}
onSubmit() {
if (!this.contactForm.valid) {
return;
}
console.log('Contact Form is Valid!', this.contactForm.getRawValue());
}
onReset(fgd: NgForm) {
fgd.onReset();
this.childForm.fgd.onReset();
}
}
contact.component.html
<mat-card>
<mat-card-title>
<h3>Contact</h3>
</mat-card-title>
<mat-card-content>
<form [formGroup]="contactForm" #fgd="ngForm" novalidate (ngSubmit)="onSubmit()">
<app-shared-form #childForm></app-shared-form>
<div class="subject">
<mat-form-field>
<input matInput placeholder="Subject" formControlName="subject">
<mat-error *ngIf="contactForm.hasError('required', 'subject')">
Please provide a subject
</mat-error>
<mat-error *ngIf="contactForm.hasError('minlength', 'subject') &&
!contactForm.hasError('required', 'subject')">
Minimum 5 characters
</mat-error>
</mat-form-field>
</div>
<div class="message">
<mat-form-field>
<textarea matInput placeholder="Message" matTextareaAutosize matAutosizeMinRows="5" matAutosizeMaxRows="10" formControlName="message">
</textarea>
<mat-error *ngIf="contactForm.hasError('required', 'message')">
Please provide a message
</mat-error>
<mat-error *ngIf="contactForm.hasError('minlength', 'message') &&
!contactForm.hasError('required', 'message')">
Minimum 10 characters
</mat-error>
</mat-form-field>
</div>
</form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button (click)="fgd.onSubmit(null)" color="primary" type="button">
Submit
</button>
<button mat-raised-button type="reset" (click)="onReset(fgd)">Reset</button>
</mat-card-actions>
</mat-card>
我创建了一个小的工作示例,使更多的东西更容易理解:https://stackblitz.com/github/SplitterAlex/stackoverflow-48931808
只需按照此处的源代码:https://github.com/SplitterAlex/stackoverflow-48931808