Используя библиотеку ngx-sub-form , вот живая демонстрация Stackblitz:
https://stackblitz.com/edit/ngx-sub-form-stepper-form-demo
Чтобы объяснить немного, это будет выглядеть следующим образом:
Во-первых, нам нужно определить некоторые интерфейсы, чтобы наш код был устойчивым и безопасным для типов
шагового form.interface.ts
export interface Part1 {
firstCtrl: string;
firstUnique: string;
}
export interface Part2 {
secondCtrl: string;
secondUnique: string;
}
export interface Part3 {
thirdUnique: string;
}
export interface StepperForm {
part1: Part1;
part2: Part2;
part3: Part3;
}
Из компонента верхнего уровня мы даже не хотим знать, что есть форма.
Мы просто хотим, чтобы нас предупреждали, когда сохранено новое значение.
app.component.ts
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
public stepperFormUpdated(stepperForm: StepperForm): void {
console.log(stepperForm);
}
}
app.component.html
<app-stepper-form (stepperFormUpdated)="stepperFormUpdated($event)"></app-stepper-form>
Теперь мы начинаем использовать библиотеку, создаем форму верхнего уровня (root) и выставляем результат в качестве вывода. Мы также определяем ограничение, что 3 уникальных входа не должны иметь одинаковые значения.
шагового form.component.ts
@Component({
selector: 'app-stepper-form',
templateUrl: './stepper-form.component.html',
styleUrls: ['./stepper-form.component.css']
})
export class StepperFormComponent extends NgxRootFormComponent<StepperForm> {
@DataInput()
@Input('stepperForm')
public dataInput: StepperForm | null | undefined;
@Output('stepperFormUpdated')
public dataOutput: EventEmitter<StepperForm> = new EventEmitter();
public send() {
this.manualSave();
}
protected getFormControls(): Controls<StepperForm> {
return {
part1: new FormControl(),
part2: new FormControl(),
part3: new FormControl(),
}
}
public getFormGroupControlOptions(): FormGroupOptions<StepperForm> {
return {
validators: [
formGroup => {
if (!formGroup || !formGroup.value || !formGroup.value.part1 || !formGroup.value.part2 || !formGroup.value.part3) {
return null;
}
const values: string[] = [
formGroup.value.part1.firstUnique,
formGroup.value.part2.secondUnique,
formGroup.value.part3.thirdUnique,
].reduce((acc, curr) => !!curr ? [...acc, curr] : acc, []);
const valuesSet: Set<string> = new Set(values);
if (values.length !== valuesSet.size) {
return {
sameValues: true
};
}
return null;
},
],
};
}
}
Время создавать наш шаблон с помощью утилит, предоставляемых lib
шагового form.component.html
<form [formGroup]="formGroup">
<mat-horizontal-stepper>
<mat-step>
<ng-template matStepLabel>First control</ng-template>
<app-first-part [formControlName]="formControlNames.part1"></app-first-part>
<button mat-button matStepperNext>Next</button>
</mat-step>
<mat-step>
<ng-template matStepLabel>Second control</ng-template>
<app-second-part [formControlName]="formControlNames.part2"></app-second-part>
<button mat-button matStepperNext>Next</button>
</mat-step>
<mat-step>
<ng-template matStepLabel>Third control</ng-template>
<app-third-part [formControlName]="formControlNames.part3"></app-third-part>
<button mat-button (click)="send()">Send the form</button>
</mat-step>
</mat-horizontal-stepper>
</form>
<div *ngIf="formGroupErrors?.formGroup?.sameValues">
Same values, please provide different ones
</div>
Теперь давайте создадим наш первый субкомпонент
первый-part.component.ts
@Component({
selector: 'app-first-part',
templateUrl: './first-part.component.html',
styleUrls: ['./first-part.component.css'],
providers: subformComponentProviders(FirstPartComponent)
})
export class FirstPartComponent extends NgxSubFormComponent<Part1> {
protected getFormControls(): Controls<Part1> {
return {
firstCtrl: new FormControl(),
firstUnique: new FormControl(),
}
}
}
и его шаблон
первый-part.component.html
<div [formGroup]="formGroup">
<mat-form-field>
<input matInput placeholder="First" type="text" [formControlName]="formControlNames.firstCtrl">
</mat-form-field>
<mat-form-field>
<input matInput type="text" placeholder="First unique" [formControlName]="formControlNames.firstUnique">
</mat-form-field>
</div>
Тогда примерно то же самое для second-part.component.html
и third-part.component.html
, поэтому я пропускаю это здесь.
Я предположил, что вам действительно не нужен FormArray
в этом случае, и я не был уверен во всем коде проверки, который у вас был, поэтому я просто создал тот, который выдает ошибки, если хотя бы 2 уникальных значения совпадают.
https://stackblitz.com/edit/ngx-sub-form-stepper-form-demo
Edit:
Если вы хотите пойти дальше, я только что опубликовал пост в блоге, который объясняет много вещей о формах и ngx-sub-форме здесь https://dev.to/maxime1992/building-scalable-robust-and-type-safe-forms-with-angular-3nf9