如何在两个组件中使用相同的验证字段?
How to use the same validation fields in two components?
我想优化我的应用程序,以免在两个组件中使用相同的验证字段。
我使用字段检查组件 "GalleryAddComponent"
中的 "title"
和 "url"
(当我添加元素时),并使用相同的字段检查 "title"
和 "url"
组件 "GalleryItemComponent"
(当我编辑组件时)。在组件中,只是表单中的按钮不同,在"GalleryAddComponent"
中有一个按钮"add"调用添加元素的方法,在组件"GalleryItemComponent"
中有按钮"updatePost" 保存元素中的更改。
我了解到需要将字段转移到单独的组件中并以组件的形式连接:"GalleryAddComponent"
和"GalleryItemComponent"
。但是如何正确地保存按钮及其逻辑的验证。请帮助理解如何实现这个想法,如果你可以在 "StackBlitz".
中设置一个例子
Link 我项目的结构:StackBlitz
GalleryAddComponent 模板:
<h3>Add new product</h3>
<div class="card">
<div class="card-body">
<form [formGroup]="angForm" novalidate>
<div class="form-group">
<label class="col-md-4">Picture Title</label>
<input type="text" class="form-control" formControlName="title" minlength="1" #title/>
</div>
<div *ngIf="angForm.controls['title'].invalid && (angForm.controls['title'].dirty || angForm.controls['title'].touched)"
class="alert alert-danger">
<div *ngIf="angForm.controls['title'].errors.required">
Title is required.
</div>
</div>
<div class="form-group">
<label class="col-md-4">Picture Address (url)</label>
<input type="url" class="form-control" formControlName="url" #url pattern="https?://.+"
title="Include http://"/>
</div>
<div *ngIf="angForm.controls['url'].invalid && (angForm.controls['url'].dirty || angForm.controls['url'].touched)"
class="alert alert-danger">
Address(url) is required.
<div *ngIf="angForm.controls['url'].errors.required ">
</div>
</div>
<div class="form-group but-group">
<button (click)="addPost(title.value, url.value); angForm.reset(title.value, url.value)"
[disabled]="angForm.pristine || angForm.invalid"
class="btn btn-primary">Add
</button>
<a routerLink="/gallery" class="btn btn-danger">Back</a>
</div>
</form>
</div>
</div>
GalleryAddComponent代码:
export class GalleryAddComponent {
angForm: FormGroup;
isAdded: boolean = false;
constructor(private fb: FormBuilder, private galleryService: GalleryService) {
this.createForm();
}
createForm(): void {
this.angForm = this.fb.group({
title: ['', Validators.required],
url: ['', Validators.required]
});
}
addPost(title: string, url: string): void {
this.galleryService.add(title, url).subscribe(res => {
this.isAdded = true;
});
}
}
GalleryItemComponent 模板:
<h4>Edit your post</h4>
<div class="card-body">
<form [formGroup]="angFormEd" novalidate>
<div class="form-group">
<label class="col-md-4">Picture Title</label>
<input type="text" class="form-control" formControlName="titleEd" #titleEd
/>
</div>
<div *ngIf="angFormEd.controls['titleEd'].invalid && (angFormEd.controls['titleEd'].dirty || angFormEd.controls['titleEd'].touched)"
class="alert alert-danger">
<div *ngIf="angFormEd.controls['titleEd'].errors.required">
Title is required.
</div>
</div>
<div class="form-group">
<label class="col-md-4">Picture Address (url)</label>
<input type="url" class="form-control" formControlName="urlEd" #urlEd pattern="https?://.+"
title="Include http://"/>
</div>
<div *ngIf="angFormEd.controls['urlEd'].invalid && (angFormEd.controls['urlEd'].dirty || angFormEd.controls['urlEd'].touched)"
class="alert alert-danger">
Address(url) is required.
<div *ngIf="angFormEd.controls['urlEd'].errors.required ">
</div>
</div>
<div class="form-group but-group">
<input type="button"
(click)="updatePost(titleEd.value, urlEd.value)"
[disabled]=" angFormEd.invalid"
class="btn btn-primary" value="Update Post">
</div>
</form>
</div>
GalleryItemComponent 代码:
export class GalleryItemComponent implements OnInit {
pic: Picture;
angFormEd: FormGroup;
constructor( private route: ActivatedRoute, private galleryService: GalleryService, private fb: FormBuilder,) {
}
ngOnInit() {
this.createFormEd();
this.showPost();
}
createFormEd(): void {
this.angFormEd = this.fb.group({
titleEd: ['', Validators.required],
urlEd: ['', Validators.required]
});
}
showPost(): void {
this.route.data.subscribe(params => {
this.pic = params.post;
this.angFormEd.setValue({titleEd: params.post.title, urlEd: params.post.url});
})
}
updatePost(title: string, url: string): void {
this.route.params.subscribe(params => {
this.galleryService.update(title, url, params['id']).subscribe(res => {
if (res.id === this.pic.id) {
this.pic.title = title;
this.pic.url = url;
}
});
});
}
}
如果您只需要创建一个可重用的验证器,请编写一个 Directive 来执行验证。通过这种方式,您可以将验证器附加到任何表单字段,只需向输入添加一个属性即可。
但是如果您想创建一个包含整个表单的可重用组件...
我建议的第一件事是使用您的表单包含的字段创建一个界面。这有助于您轻松地在组件之间传递数据。
export interface GalleryItem {
title: string;
url: string;
}
然后您必须创建一个包含表单和提交按钮的子组件。该组件的作用是获取用户的输入,对其进行验证,如果可以,则将其返回给父组件,可能是 AddComponent
或 EditComponent
。父组件决定如何处理这些数据。如果是 Edit
页面,父组件应该首先获取以前的数据并将其传递给子组件以填充表单。按钮标签也可以从父级传递给子级。
多种组件交互方式供您选择。如果您不熟悉在组件之间传递数据,我强烈建议您阅读 Angular docs, especially the Component Interaction 部分。
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
import { FormGroup, FormControl, FormArray, AbstractControl } from '@angular/forms'
@Component({
selector: 'control-error-message',
host: {
'[class.error_msg]': 'showErrorMessage'
},
template: `<span *ngIf="showErrorMessage">{{displayMessage}}</span>`
})
export class ErrorMessageComponent {
@Input() control: FormControl;
errorMessage: string;
displayMessage: string;
showErrorMessage: boolean = false;
constructor() { }
ngOnChanges(change: SimpleChanges) {
if (this.control) {
this.control.valueChanges.subscribe(
(res) => {
this.setErrorMessage(this.control);
}
)
}
}
getControlName(c: AbstractControl): string | null {
const formGroup = c.parent.controls;
return Object.keys(formGroup).find(name => c === formGroup[name]) || null;
}
setErrorMessage(control: FormControl) {
this.errorMessage = "";
if (control.errors) {
for (let propertyName in control.errors) {
if (propertyName.toLowerCase() == "required") {
this.displayMessage = this.getControlName(control) + " field is required";
this.showErrorMessage = true;
}
}
}
}
}
在html你可以有类似
的东西
<form [formGroup]="angFormEd" novalidate>
<div class="form-group">
<label class="col-md-4">Picture Title</label>
<input type="text" class="form-control" formControlName="title" minlength="1" #title/>
<control-error-message [control]="formGroupObject.get('title')"></control-error-message>
</div>
这未经测试或工作代码,我删除了几行代码以使其看起来更简单明了。
您不必在 html:
中引入这一行
<div *ngIf="angForm.controls['title'].invalid && (angForm.controls['title'].dirty || angForm.controls['title'].touched)"
class="alert alert-danger">
<div *ngIf="angForm.controls['title'].errors.required">
Title is required.
</div>
</div>
如果出现错误,所有样式将由 "error_msg" class 应用到主机上来处理。另外,消息将显示在输入字段的正下方。
注意:我刚刚复制粘贴了我的代码并删除了一些东西,理解这个想法并根据您的要求实现它
我想优化我的应用程序,以免在两个组件中使用相同的验证字段。
我使用字段检查组件 "GalleryAddComponent"
中的 "title"
和 "url"
(当我添加元素时),并使用相同的字段检查 "title"
和 "url"
组件 "GalleryItemComponent"
(当我编辑组件时)。在组件中,只是表单中的按钮不同,在"GalleryAddComponent"
中有一个按钮"add"调用添加元素的方法,在组件"GalleryItemComponent"
中有按钮"updatePost" 保存元素中的更改。
我了解到需要将字段转移到单独的组件中并以组件的形式连接:"GalleryAddComponent"
和"GalleryItemComponent"
。但是如何正确地保存按钮及其逻辑的验证。请帮助理解如何实现这个想法,如果你可以在 "StackBlitz".
Link 我项目的结构:StackBlitz
GalleryAddComponent 模板:
<h3>Add new product</h3>
<div class="card">
<div class="card-body">
<form [formGroup]="angForm" novalidate>
<div class="form-group">
<label class="col-md-4">Picture Title</label>
<input type="text" class="form-control" formControlName="title" minlength="1" #title/>
</div>
<div *ngIf="angForm.controls['title'].invalid && (angForm.controls['title'].dirty || angForm.controls['title'].touched)"
class="alert alert-danger">
<div *ngIf="angForm.controls['title'].errors.required">
Title is required.
</div>
</div>
<div class="form-group">
<label class="col-md-4">Picture Address (url)</label>
<input type="url" class="form-control" formControlName="url" #url pattern="https?://.+"
title="Include http://"/>
</div>
<div *ngIf="angForm.controls['url'].invalid && (angForm.controls['url'].dirty || angForm.controls['url'].touched)"
class="alert alert-danger">
Address(url) is required.
<div *ngIf="angForm.controls['url'].errors.required ">
</div>
</div>
<div class="form-group but-group">
<button (click)="addPost(title.value, url.value); angForm.reset(title.value, url.value)"
[disabled]="angForm.pristine || angForm.invalid"
class="btn btn-primary">Add
</button>
<a routerLink="/gallery" class="btn btn-danger">Back</a>
</div>
</form>
</div>
</div>
GalleryAddComponent代码:
export class GalleryAddComponent {
angForm: FormGroup;
isAdded: boolean = false;
constructor(private fb: FormBuilder, private galleryService: GalleryService) {
this.createForm();
}
createForm(): void {
this.angForm = this.fb.group({
title: ['', Validators.required],
url: ['', Validators.required]
});
}
addPost(title: string, url: string): void {
this.galleryService.add(title, url).subscribe(res => {
this.isAdded = true;
});
}
}
GalleryItemComponent 模板:
<h4>Edit your post</h4>
<div class="card-body">
<form [formGroup]="angFormEd" novalidate>
<div class="form-group">
<label class="col-md-4">Picture Title</label>
<input type="text" class="form-control" formControlName="titleEd" #titleEd
/>
</div>
<div *ngIf="angFormEd.controls['titleEd'].invalid && (angFormEd.controls['titleEd'].dirty || angFormEd.controls['titleEd'].touched)"
class="alert alert-danger">
<div *ngIf="angFormEd.controls['titleEd'].errors.required">
Title is required.
</div>
</div>
<div class="form-group">
<label class="col-md-4">Picture Address (url)</label>
<input type="url" class="form-control" formControlName="urlEd" #urlEd pattern="https?://.+"
title="Include http://"/>
</div>
<div *ngIf="angFormEd.controls['urlEd'].invalid && (angFormEd.controls['urlEd'].dirty || angFormEd.controls['urlEd'].touched)"
class="alert alert-danger">
Address(url) is required.
<div *ngIf="angFormEd.controls['urlEd'].errors.required ">
</div>
</div>
<div class="form-group but-group">
<input type="button"
(click)="updatePost(titleEd.value, urlEd.value)"
[disabled]=" angFormEd.invalid"
class="btn btn-primary" value="Update Post">
</div>
</form>
</div>
GalleryItemComponent 代码:
export class GalleryItemComponent implements OnInit {
pic: Picture;
angFormEd: FormGroup;
constructor( private route: ActivatedRoute, private galleryService: GalleryService, private fb: FormBuilder,) {
}
ngOnInit() {
this.createFormEd();
this.showPost();
}
createFormEd(): void {
this.angFormEd = this.fb.group({
titleEd: ['', Validators.required],
urlEd: ['', Validators.required]
});
}
showPost(): void {
this.route.data.subscribe(params => {
this.pic = params.post;
this.angFormEd.setValue({titleEd: params.post.title, urlEd: params.post.url});
})
}
updatePost(title: string, url: string): void {
this.route.params.subscribe(params => {
this.galleryService.update(title, url, params['id']).subscribe(res => {
if (res.id === this.pic.id) {
this.pic.title = title;
this.pic.url = url;
}
});
});
}
}
如果您只需要创建一个可重用的验证器,请编写一个 Directive 来执行验证。通过这种方式,您可以将验证器附加到任何表单字段,只需向输入添加一个属性即可。
但是如果您想创建一个包含整个表单的可重用组件...
我建议的第一件事是使用您的表单包含的字段创建一个界面。这有助于您轻松地在组件之间传递数据。
export interface GalleryItem {
title: string;
url: string;
}
然后您必须创建一个包含表单和提交按钮的子组件。该组件的作用是获取用户的输入,对其进行验证,如果可以,则将其返回给父组件,可能是 AddComponent
或 EditComponent
。父组件决定如何处理这些数据。如果是 Edit
页面,父组件应该首先获取以前的数据并将其传递给子组件以填充表单。按钮标签也可以从父级传递给子级。
多种组件交互方式供您选择。如果您不熟悉在组件之间传递数据,我强烈建议您阅读 Angular docs, especially the Component Interaction 部分。
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
import { FormGroup, FormControl, FormArray, AbstractControl } from '@angular/forms'
@Component({
selector: 'control-error-message',
host: {
'[class.error_msg]': 'showErrorMessage'
},
template: `<span *ngIf="showErrorMessage">{{displayMessage}}</span>`
})
export class ErrorMessageComponent {
@Input() control: FormControl;
errorMessage: string;
displayMessage: string;
showErrorMessage: boolean = false;
constructor() { }
ngOnChanges(change: SimpleChanges) {
if (this.control) {
this.control.valueChanges.subscribe(
(res) => {
this.setErrorMessage(this.control);
}
)
}
}
getControlName(c: AbstractControl): string | null {
const formGroup = c.parent.controls;
return Object.keys(formGroup).find(name => c === formGroup[name]) || null;
}
setErrorMessage(control: FormControl) {
this.errorMessage = "";
if (control.errors) {
for (let propertyName in control.errors) {
if (propertyName.toLowerCase() == "required") {
this.displayMessage = this.getControlName(control) + " field is required";
this.showErrorMessage = true;
}
}
}
}
}
在html你可以有类似
的东西<form [formGroup]="angFormEd" novalidate>
<div class="form-group">
<label class="col-md-4">Picture Title</label>
<input type="text" class="form-control" formControlName="title" minlength="1" #title/>
<control-error-message [control]="formGroupObject.get('title')"></control-error-message>
</div>
这未经测试或工作代码,我删除了几行代码以使其看起来更简单明了。 您不必在 html:
中引入这一行<div *ngIf="angForm.controls['title'].invalid && (angForm.controls['title'].dirty || angForm.controls['title'].touched)"
class="alert alert-danger">
<div *ngIf="angForm.controls['title'].errors.required">
Title is required.
</div>
</div>
如果出现错误,所有样式将由 "error_msg" class 应用到主机上来处理。另外,消息将显示在输入字段的正下方。
注意:我刚刚复制粘贴了我的代码并删除了一些东西,理解这个想法并根据您的要求实现它