在线性矩阵水平步进器中使用单独的组件
Posted
技术标签:
【中文标题】在线性矩阵水平步进器中使用单独的组件【英文标题】:Using separate components in a linear mat-horizontal-stepper 【发布时间】:2018-04-29 01:55:25 【问题描述】:在 Angular 中,是否可以有一个线性步进器,其中各个步骤是单独的组件?例如:
<mat-horizontal-stepper [linear]="isLinear">
<mat-step [stepControl]="firstFormGroup" label="Some Form">
<first-component></first-component>
</mat-step>
<mat-step [stepControl]="secondFormGroup" label="Another Form">
<second-component></second-component>
</mat-step>
<mat-step [stepControl]="thirdFormGroup" label="Review">
<third-component></third-component>
</mat-step>
</mat-horizontal-stepper>
当我尝试这个时,我在点击matStepperNext
按钮时收到以下错误:
TypeError: Cannot read property 'invalid' of undefined
.
【问题讨论】:
我们可以看看你的component.ts代码吗?我的猜测是组件中未定义一个或多个 stepcontrol 变量。 @BrianWright 所以我试图在 plnkr 上整理一个最小的例子,但我一定搞砸了,因为它告诉我它找不到其中一个组件......embed.plnkr.co/5Yx4RTIrIHklRtH5rJHO(与我上面所说的问题完全不同)。 别担心,请看下面我的回答。另外,请查看:stackblitz.com @BrianWright 谢谢! Stackblitz 比 plnkr 容易得多。所以,我做了下面的例子。我实际上遇到了一个单独的问题 -linear
属性 mat-horizontal-stepper
没有被强制执行。即使表单无效,它也允许您跳到后续步骤。但是,我怀疑如果不是这种情况,您会看到同样的问题(点击下一步按钮时的错误)。这是示例:stackblitz.com/edit/angular-fjhcgm
没问题!是的,这好多了。待会儿我再看看,除非有人抢先一步!
【参考方案1】:
您可以使用子表单来解决它。 实际上,几个月前我在 Angular-UP 会议上发表了一篇关于它的演讲: https://www.youtube.com/watch?v=sb7tgsNF2Jk
一般的思路是在子组件中创建表单,使用DI注入controlContainer并将本地表单设置为controlContainer表单。
子组件:
@Component(
selector: 'app-company-info',
templateUrl: './company-info.component.html',
styleUrls: ['./company-info.component.scss'],
viewProviders: [ provide: ControlContainer, useExisting: FormGroupDirective ]
)
export class CompanyInfoComponent implements OnInit
form: FormGroup;
constructor(
private ctrlContainer: FormGroupDirective,
private fb: FormBuilder)
ngOnInit()
this.form = this.ctrlContainer.form;
this.form.addControl('company',
this.fb.group(
'companyName': this.fb.control(null, [Validators.required]),
'numOfEmployees': this.fb.control(null, [Validators.required]));
父组件 (html):
<mat-horizontal-stepper [linear]="isLinear" #stepper>
<mat-step [stepControl]="companyInfo">
<ng-template matStepLabel>Fill out your name</ng-template>
<form [formGroup]="companyInfo">
<app-company-info></app-company-info>
</form>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</mat-step>
</mat-horizontal-stepper>
父组件 (ts):
export class WizardComponent implements OnInit
isLinear = true;
companyInfo: FormGroup;
constructor(private _formBuilder: FormBuilder)
ngOnInit()
this.companyInfo = this._formBuilder.group(
);
【讨论】:
您能提供一个工作示例吗?我已经准备好了这个stackblitz,所以应该很快:) 使用子表单改进两个答案。这是stackblitz 上的示例【参考方案2】:这是适合我的解决方案。
<mat-horizontal-stepper [linear]="true" #stepper>
<mat-step [stepControl]="selectAdvType">
<ng-template matStepLabel>
<div class="text-center">
<mat-icon>queue_play_next</mat-icon><br /><span>Select Adv Type</span>
</div>
</ng-template>
<app-advertisement-type></app-advertisement-type>
</mat-step>
<mat-step [stepControl]="selectAdvType">
<ng-template matStepLabel>
<div class="text-center">
<mat-icon>directions_car</mat-icon><br /><span>Select Car</span>
</div>
</ng-template>
<app-select-car-adv></app-select-car-adv>
</mat-step>
<mat-step>
<ng-template matStepLabel>
<div class="text-center">
<mat-icon>description</mat-icon><br /><span>Select Features</span>
</div>
</ng-template>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
</mat-step>
</mat-horizontal-stepper>
父 Ts 文件
@Component(
selector: 'app-customer.create.advertisement',
templateUrl: './customer.create.advertisement.component.html',
styleUrls: ['./customer.create.advertisement.component.scss']
)
export class CustomerCreateAdvertisementComponent implements OnInit
isLinear = false;
selectAdvType: FormGroup;
constructor(private _formBuilder: FormBuilder)
ngOnInit()
this.selectAdvType = this._formBuilder.group(
firstCtrl: ['', Validators.required]
);
子组件
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>Fill out your name</ng-template>
<mat-form-field>
<input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
</mat-form-field>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</form>
@Component(
selector: 'app-advertisement-type',
templateUrl: './advertisement-type.component.html',
styleUrls: ['./advertisement-type.component.scss']
)
export class AdvertisementTypeComponent implements OnInit
firstFormGroup: FormGroup;
constructor(private _formBuilder: FormBuilder)
ngOnInit()
this.firstFormGroup = this._formBuilder.group(
firstCtrl: ['', Validators.required]
);
【讨论】:
为什么我们需要在下面的表单构建器'firstCtrl: ['', Validators.required]'中定义两次(父组件和子组件ts文件)。假设如果我在子元素表单中有 10 个项目,它是否也在父组件中配置?【参考方案3】:改进@eliran-eliassy 答案和@christian-steinmeyer 问题。
Parent.Component.ts
export class ParentComponent implements OnInit
isLinear = true;
companyInfo: FormGroup;
constructor(private _formBuilder: FormBuilder)
ngOnInit()
this.companyInfo = this._formBuilder.group(
);
Parent.Component.html
<mat-horizontal-stepper [linear]="isLinear" #stepper>
<mat-step [stepControl]="companyInfo">
<form [formGroup]="companyInfo">
<ng-template matStepLabel>Fill out your name</ng-template>
<app-company-info></app-company-info>
</form>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</mat-step>
</mat-horizontal-stepper>
Child.Component.ts -> 这是子表单
export class ChildComponent implements OnInit
form: FormGroup;
subForm: FormGroup;
constructor(
private ctrlContainer: FormGroupDirective,
private fb: FormBuilder
)
ngOnInit()
this.subForm = this.fb.group(
companyName: [null, [Validators.required]],
numOfEmployees: [null, [Validators.required]]
);
this.form = this.ctrlContainer.form;
this.form.addControl("company", this.subForm);
Child.Component.html
<div [formGroup]="subForm">
<mat-form-field appearance="outline">
<input matInput placeholder="Your Company Name" formControlName="companyName">
</mat-form-field>
</div>
在stackblitz上查看此解决方案
【讨论】:
【参考方案4】:好的,我想我发现了一些问题:
<mat-horizontal-stepper [linear]="isLinear">
<mat-step [stepControl]="foodFormGroup">
<food-selection></food-selection>
</mat-step>
<mat-step [stepControl]="pieFormGroup">
<pie-selection></pie-selection>
</mat-step>
</mat-horizontal-stepper>
foodFormGroup 和 pieFormGroup 需要在您的 tough-choice.component.ts 文件中定义(顺便说一句,在您的示例中拼写错误代码)
这是一个示例(来自文档):
import Component from '@angular/core';
import FormBuilder, FormGroup, Validators from '@angular/forms';
/**
* @title Stepper overview
*/
@Component(
selector: 'stepper-overview-example',
templateUrl: 'stepper-overview-example.html',
styleUrls: ['stepper-overview-example.css']
)
export class StepperOverviewExample
isLinear = false;
firstFormGroup: FormGroup;
secondFormGroup: FormGroup;
constructor(private _formBuilder: FormBuilder)
ngOnInit()
this.firstFormGroup = this._formBuilder.group(
firstCtrl: ['', Validators.required]
);
this.secondFormGroup = this._formBuilder.group(
secondCtrl: ['', Validators.required]
);
另外,我在您的示例中没有看到 module.ts 文件。那是您想要导入 @angular/material 模块的地方,而不是在组件文件中。
我建议只看一遍 Angular Material 文档。 https://material.angular.io/components/stepper/overview
【讨论】:
谢谢。我很欣赏对错字的提醒,这让我陷入了困境。我通过 stackblitz 演示回复了您对主帖的评论。以上是关于在线性矩阵水平步进器中使用单独的组件的主要内容,如果未能解决你的问题,请参考以下文章
带有 selectedIndex 设置 matStepperNext/matStepperPrevious 的角步进器不起作用