我正在使用Angular Reactive表单,并且有如下数据:
detail = {
customerNo: '1231234123412',
nameKanji: 'test0',
nameKana: 'かな',
gender: '0',
birthday: '2019-02-02',
telNo: '79-0999-1111',
addressZip: '120-0813',
address: 'takenotsuka0',
email: 'slack@test.com',
point: '1231230',
family: [
{ name: 'テスト0', relationship: "wife" },
{ name: 'テスト1', relationship: "brother" }
]
};
我已经如下定义了我的formGroup
this.detailForm = this.fb.group(
{
global: [null],
customerNo: [
null,
[Validators.required, StringValidator.equalLength(13)]
],
nameKanji: [null, [Validators.required, Validators.maxLength(60)]],
nameKana: [null, [Validators.required, Validators.maxLength(60)]],
gender: ['1', [Validators.required]],
birthday: [null, [Validators.required]],
addressZip: [null, []],
telNo: [null, [Validators.required, StringValidator.equalLength(13)]],
address: [null],
email: [null, [Validators.email, Validators.maxLength(100)]],
point: [null, [Validators.required]],
family: this.fb.array([])
},
{ updateOn: 'blur' }
);
public addFamilyGroup() {
return this.fb.group({
name: [null, [Validators.required]],
relationship: [null, [Validators.required]]
});
}
get familyArray(): FormArray {
return <FormArray>this.detailForm.get('family');
}
您可以看到family是数据结构中的嵌套数组,现在,如果我已经具有family.wife值,我想为其添加自定义验证。
答案 0 :(得分:1)
您需要在formArray上创建一个自定义的表单验证器,在this SO的最后一部分中有一个示例
创建表单数组的customFormArray总是一样的(我喜欢将自定义表单错误作为函数放入组件本身中
allDifferent()
{
return (formArray:FormArray)=>{
//we create an array only with the "relationShip"
const value=formArray.value?formArray.value.map(x=>x.relationship):null
//(value.filter(...) create an array with the repeat values
//so, we check the length to return null or an object
return value &&
(value.filter((item, index) => item && value.indexOf(item) != index)).length>0?
{error:"values repeat"}:null
}
}
当您创建表单时
family: this.fb.array([],this.allDifferent())
更新,如果我们想要重复索引,我们可以创建一个自定义formError,该错误返回带有重复索引的数组“错误”,请记住,我们选择了想要返回的内容
allDifferentIndex()
{
return (formArray:FormArray)=>{
//we create an array of objects [{relationship:..,index:..},{relationship:..,index:...}..]
const value=formArray.value?formArray.value
.map((x,index)=>({relationship:x.relationship,index:index})):null
if (value)
{
//we filter the array only with the values repeats
const repeatValues=(value.filter((item, index) => item.relationship &&
value.findIndex(f=>f.relationship==item.relationship) != index))
//if the array length>0 return an object {error:value}
// the value is an array of numbers, we use map to return only the "index"
return repeatValues.length?{error:repeatValues.map(x=>x.index)}:null
}
}
}
我们可以使用类似
<div formArrayName="family">
<div *ngFor="let group of familyArray.controls;let i=index" [formGroupName]="i">
<input formControlName="relationship">
<span *ngIf="familyArray.errors?.error && familyArray.errors.error.indexOf(i)>=0">
*
</span>
</div>
</div>
并使用
family: this.fb.array([],this.allDifferentIndex())
更新2 ,我们可以创建一个更通用的customError,只是一个函数
export function allDifferentIndex(property:string)
{
return (formArray:FormArray)=>{
const value=formArray.value?formArray.value.map((x,index)=>({field:x[property],index:index})):null
if (value)
{
const repeatValues=(value.filter((item, index) => item.field && value.findIndex(f=>f.field==item.field) != index))
return repeatValues.length?{error:repeatValues.map(x=>x.index)}:null
}
}
}
并用作
family: this.fb.array([],allDifferentIndex('relationship'))
一个简单的stackblitz using inputs-我使用了最后一个方法,一个外部函数,但是我在代码中留给了其他人
答案 1 :(得分:0)
非常感谢。 我尝试下面的代码。当我两次选择“妻子选项”时,浏览器现在显示错误消息。但是当我将选择框更改为另一个选项时,错误消息不会消失。
以html
<small id="fnHelp" *ngIf="familyArray.invalid && familyArray.touched" class="form-text custom-invalid">error</small>
在component.ts
中 public checkValidator = (c: AbstractControl) => {
const getValue = this.detailForm.value.family;
if (Object.keys(getValue).length >= 2) {
console.log('valid', this.familyArray);
return { error: '配偶者を二人以上登録することができません' };
}
return null;
};
和familyArray.errors的控制台似乎总是错误的
也。该消息会显示在所有选择框中,而不是我选择的那个框中。
我已将所有代码粘贴到代码段中
import { differenceInCalendarDays, format } from 'date-fns';
import { NzMessageService } from 'ng-zorro-antd';
import { untilDestroyed } from 'ngx-take-until-destroy';
import { filter } from 'rxjs/operators';
import { CustomerDetailQuery } from 'src/business/queries/customer-detail.query';
import { CustomerDetailService } from 'src/business/services/customer-detail.service';
import { CustomerService } from 'src/business/services/customer.service';
import {
CustomerDetail,
Relationship
} from 'src/business/states/customer-detail.model';
import { CustomerDetailStore } from 'src/business/states/customer-detail.store';
import { BasePage, StringValidator, ValidationError } from '@acts/front-core';
import {
Component,
ElementRef,
Injector,
OnDestroy,
OnInit,
ViewChild
} from '@angular/core';
import {
AbstractControl,
FormArray,
FormBuilder,
FormControl,
FormGroup,
ValidatorFn,
Validators
} from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
/**
* 顧客詳細画面のComponent
*/
@Component({
selector: 'app-customer-detail',
templateUrl: './customer-detail.component.html',
styleUrls: ['./customer-detail.component.scss']
})
export class CustomerDetailComponent extends BasePage
implements OnInit, OnDestroy {
@ViewChild('detailCard', { static: false, read: ElementRef })
public customerCard: ElementRef;
public _this: any = this;
/** 顧客詳細情報(入力内容) */
public detailForm: FormGroup;
// 続柄配列
public relationships: Array<string>;
// 家族情報ポップアップ
public isVisible: boolean;
// Default時間
public birthdayDate = new Date(2000, 1, 1);
public storeLoading$ = this.customerDetailQuery.selectLoading();
public showLoading = false;
public readonly dateFormat = 'yyyy年M月d日';
public routeParam = {
refresh: null,
customerNo: null,
isEditMode: false
};
constructor(
private customerDetailQuery: CustomerDetailQuery,
private customerDetailService: CustomerDetailService,
private customerDetailStore: CustomerDetailStore,
private customerSearchService: CustomerService,
private message: NzMessageService,
private router: Router,
private activatedRoute: ActivatedRoute,
private fb: FormBuilder,
private translate: TranslateService,
injector: Injector
) {
super(injector);
}
/**
* Component初期処理
*
* @memberof CustomerDetailComponent
*/
public ngOnInit(): void {
this.initForm();
this.setErrorSubscribe();
this.relationships = ['配偶者', '子供', '孫', 'その他'];
this.initData();
}
/**
* Component破棄時の処理
*
* @memberof CustomerDetailComponent
*/
public ngOnDestroy(): void {}
/**
* 顧客情報の保存内容を送信する(新規登録・更新する)
*
* @memberof CustomerDetailComponent
*/
public submitForm(): void {
const controls = this.detailForm.controls;
for (const key in controls) {
if (controls.hasOwnProperty(key)) {
controls[key].markAsTouched();
controls[key].updateValueAndValidity();
}
}
setTimeout(() => {
// form validation check
if (this.detailForm.valid) {
const value = this.getFinalData();
// send request
if (this.routeParam.customerNo) {
this.update(value);
} else {
this.create(value);
}
} else {
this.message.create(
'error',
this.translate.instant('SystemMessages.ValidationErrorTip')
);
}
}, 0);
}
/**
* 施設予約画面へ遷移する
*
* @param {MouseEvent} event
*/
public navigateToFacilityPage(): void {
const customerNo = this.customerDetailQuery.getValue().customerNo;
if (customerNo) {
// navigate
this.router.navigate(['/acts-demo/facility/book']);
}
}
* 初期化家族テーブル
*
* @memberof CustomerDetailComponent
*/
public initialFamilyTable(length: number): void {
if (length === 0) {
this.addFamilyInfo();
}
}
public saveFamilyInfo(): void {
this.isVisible = false;
}
public cancelFamilyInfo(): void {
this.isVisible = false;
}
private initForm(): void {
this.detailForm = this.fb.group(
{
global: [null],
customerNo: [
null,
[Validators.required, StringValidator.equalLength(13)]
],
nameKanji: [null, [Validators.required, Validators.maxLength(60)]],
nameKana: [null, [Validators.required, Validators.maxLength(60)]],
gender: ['1', [Validators.required]],
birthday: [null, [Validators.required]],
addressZip: [null, []],
telNo: [null, [Validators.required, StringValidator.equalLength(13)]],
address: [null],
email: [null, [Validators.email, Validators.maxLength(100)]],
point: [null, [Validators.required]],
family: this.fb.array([])
},
{ updateOn: 'blur' }
);
}
/**
* Family Control Groupを追加
*
* @public
* @memberof CustomerDetailComponent
*/
// tslint:disable-next-line:typedef
public addFamilyGroup() {
return this.fb.group({
name: [null, []],
relationship: [null, [this.checkValidator]]
});
}
get familyArray(): FormArray {
return <FormArray>this.detailForm.get('family');
}
/**
* 家族情報を追加
*
* @public
* @memberof CustomerDetailComponent
*/
public addFamilyInfo(): void {
this.familyArray.push(this.addFamilyGroup());
}
/**
* 家族情報を削除
*
* @public
* @memberof CustomerDetailComponent
*/
public deleteFamilyInfo(index: number): void {
this.familyArray.removeAt(index);
}
/**
*
* load FamilyArray
* @public
* @memberof CustomerDetailComponent
*/
public loadFamilyForm(data: CustomerDetail): void {
for (let i = 0; i < data.family.length - 1; i++) {
this.familyArray.push(this.addFamilyGroup());
}
}
/**
*
* バリデーション追加
* @public
* @memberof CustomerDetailComponent
*/
public checkValidator = (c: AbstractControl) => {
const getValue = this.detailForm.value.family;
if (Object.keys(getValue).length >= 2) {
console.log('valid', this.familyArray);
return { errors: '配偶者を二人以上登録することができません' };
}
return null;
};
private initData(): void {
this.activatedRoute.queryParams.subscribe((res: any) => {
if (res.customerNo) {
this.routeParam.customerNo = res.customerNo;
this.customerDetailService.getCustomerDetailByID(res.customerNo);
}
if (res.refresh) {
this.routeParam.refresh = res.refresh;
}
});
this.customerDetailQuery
.select()
.pipe(
untilDestroyed(this),
filter(item => item.customerNo !== '')
)
.subscribe((detail: any) => {
console.log('detailForm', this.detailForm);
detail = {
customerNo: '1231234123412',
nameKanji: 'test0',
nameKana: 'かな',
gender: '0',
birthday: '2019-02-02',
telNo: '79-0999-1111',
addressZip: '120-0813',
address: 'takenotsuka0',
email: 'slack@test.com',
point: '1231230',
family: [
{
name: 'テスト1',
relationship: Relationship.wife
},
{
name: 'テスト2',
relationship: Relationship.child
}
]
};
this.showLoading = false;
if (detail.error !== null && detail.error) {
console.log('detail error return');
return;
}
if (this.routeParam.refresh) {
this.customerSearchService.updateCustomer(
this.routeParam.customerNo,
detail
);
}
if (this.routeParam.customerNo !== null) {
this.detailForm.markAsUntouched();
this.detailForm.markAsPristine();
console.log('detail', detail);
this.loadFamilyForm(detail);
this.detailForm.patchValue(detail);
this.detailForm.patchValue({ telNo: this.addHyphen(detail.telNo) });
this.routeParam.isEditMode = true;
this.initialFamilyTable(this.detailForm.value.family.length);
} else {
this.customerDetailStore.reset();
this.initialFamilyTable(this.detailForm.value.family.length);
}
});
}
private setErrorSubscribe(): void {
this.customerDetailQuery
.selectError()
.pipe(
untilDestroyed(this),
filter(error => error !== undefined && error !== null)
)
.subscribe((error: ValidationError) => {
this.setFormError(this.detailForm, error, false);
this.message.create(
'error',
this.translate.instant('SystemMessages.ValidationErrorTip')
);
this.showLoading = false;
});
this.detailForm.valueChanges.pipe(untilDestroyed(this)).subscribe(() => {
this.detailForm.controls['global'].setErrors(null);
});
}
private navBackAndRefresh(): void {
this.router.navigate(['/acts-demo/customer/search'], {
queryParams: {
refresh: new Date().getTime()
}
});
}
private create(value: CustomerDetail): void {
this.showLoading = true;
this.customerDetailService.create(value).subscribe(() => {
this.showLoading = false;
this.navBackAndRefresh();
});
}
private update(value: any): void {
this.showLoading = true;
this.customerDetailService.update(value).subscribe((result: boolean) => {
if (result) {
this.navigateBack();
}
this.showLoading = false;
});
}
private getFinalData(): CustomerDetail {
const value = this.detailForm.value as CustomerDetail;
value.telNo = this.removeHyphen(value.telNo);
value.birthday = this.formatDate(value.birthday);
return value;
}
/**
* Format date
*
* @private
*/
private formatDate(date: any): any {
let temp = date;
if (typeof date === 'object') {
temp = format(date, 'yyyy-MM-dd');
}
return temp;
}
/**
* delete Hyphen
*
* @private
* @param {string} code
* @returns {string}
* @memberof CustomerDetailComponent
*/
private removeHyphen(code: string): string {
if (!code || code.length === 0) {
return '';
}
return code.replace(/-/g, '');
}
/**
* Add hyphen
*
* @private
* @param {string} targetStr
* @returns {string}
* @memberof CustomerDetailComponent
*/
private addHyphen(targetStr: string): string {
let changedStr = targetStr;
if (changedStr.length === 0) {
changedStr = '';
} else if (changedStr.length <= 3) {
changedStr = changedStr.replace(/^(\d{0,3})/, '$1');
} else if (changedStr.length <= 7) {
changedStr = changedStr.replace(/^(\d{0,3})(\d{0,4})/, '$1-$2');
} else {
changedStr = changedStr.replace(/^(\d{0,3})(\d{0,4})(.*)/, '$1-$2-$3');
}
return changedStr;
}
}
<nz-card [nzBordered]="false" #detailCard class="form-container">
<nz-spin [nzSpinning]="(storeLoading$ | async) || showLoading">
<h3>{{ 'CustomerDetail.header.title' | translate }}</h3>
<div padding>
<global-error-message
[form]="detailForm"
lines="none"
></global-error-message>
</div>
<form nz-form [formGroup]="detailForm">
<!-- title -->
<h4>{{ 'CustomerDetail.form.title' | translate }}</h4>
<p>{{ 'CustomerDetail.form.info' | translate }}</p>
<!-- customerNo input -->
<nz-form-item class="border">
<div class="form-title required">
{{ 'CustomerDetail.form.customerNo.title' | translate }}
</div>
<div class="form-subtitle"></div>
<div class="form-content">
<div class="form-content-item">
<nz-form-label nzFor="customerNo"
>{{ 'CustomerDetail.form.customerNo.subTitle' | translate }}
</nz-form-label>
<nz-form-control>
<input
nz-input
formControlName="customerNo"
id="customerNo"
[disabled]="routeParam.isEditMode"
[readonly]="routeParam.isEditMode"
maxlength="13"
placeholder="{{
'CustomerDetail.form.customerNo.placeholder' | translate
}}"
/>
</nz-form-control>
</div>
</div>
</nz-form-item>
<div>
<validation-messages
minHeight
[control]="detailForm.controls['customerNo']"
[fieldName]="'CustomerDetail.form.customerNo.title'"
>
</validation-messages>
</div>
<!-- name input -->
<nz-form-item class="border">
<div class="form-title required">
{{ 'CustomerDetail.form.name.title' | translate }}
</div>
<div class="form-subtitle">
<div class="form-subtitle-item">
<nz-form-label [nzNoColon]="true" nzFor="nameKanji">{{
'CustomerDetail.form.name.nameKanjiTitle' | translate
}}</nz-form-label>
</div>
<div class="form-subtitle-item">
<nz-form-label [nzNoColon]="true" nzFor="nameKana">{{
'CustomerDetail.form.name.nameKanaTitle' | translate
}}</nz-form-label>
</div>
</div>
<div class="form-content">
<div class="form-content-item">
<nz-form-label nzFor="nameKanji">{{
'CustomerDetail.form.fullWidth' | translate
}}</nz-form-label>
<nz-form-control>
<input
nz-input
formControlName="nameKanji"
id="nameKanji"
placeholder="{{
'CustomerDetail.form.name.nameKanjiPlaceholder' | translate
}}"
/>
</nz-form-control>
</div>
<div class="form-content-item">
<nz-form-label nzFor="nameKana">{{
'CustomerDetail.form.fullWidthKana' | translate
}}</nz-form-label>
<nz-form-control>
<input
nz-input
formControlName="nameKana"
id="nameKana"
placeholder="{{
'CustomerDetail.form.name.nameKanaPlaceholder' | translate
}}"
/>
</nz-form-control>
</div>
</div>
</nz-form-item>
<div>
<validation-messages
minHeight
[control]="detailForm.controls['nameKanji']"
[fieldName]="'CustomerDetail.form.name.nameKanjiTitle'"
>
</validation-messages>
<validation-messages
minHeight
[control]="detailForm.controls['nameKana']"
[fieldName]="'CustomerDetail.form.name.nameKanaTitle'"
>
</validation-messages>
</div>
<!-- gender -->
<nz-form-item class="border">
<div class="form-title required">
{{ 'CustomerDetail.form.gender.title' | translate }}
</div>
<div class="form-subtitle"></div>
<div class="form-content">
<div class="form-content-item">
<nz-form-label nzFor="gender">{{
'CustomerDetail.form.gender.subTitle' | translate
}}</nz-form-label>
<nz-form-control>
<nz-radio-group formControlName="gender" id="gender">
<label nz-radio nzValue="1">{{
'Common.gender.male' | translate
}}</label>
<label nz-radio nzValue="2">{{
'Common.gender.female' | translate
}}</label>
</nz-radio-group>
</nz-form-control>
</div>
</div>
</nz-form-item>
<!-- date -->
<nz-form-item class="border">
<div class="form-title required">
{{ 'CustomerDetail.form.birthday.title' | translate }}
</div>
<div class="form-subtitle"></div>
<div class="form-content">
<div class="form-content-item">
<nz-form-label nzFor="birthday">{{
'CustomerDetail.form.birthday.subTitle' | translate
}}</nz-form-label>
<nz-form-control [nzSpan]="7">
<nz-date-picker
formControlName="birthday"
id="birthday"
[nzFormat]="dateFormat"
(ngModelChange)="onBirthdayChange($event)"
nzPlaceHolder="{{
'CustomerDetail.form.birthday.placeholder' | translate
}}"
[nzDisabledDate]="disabledDate"
>
</nz-date-picker>
</nz-form-control>
<nz-form-control [nzSpan]="7">
<label>({{ birthdayDate | era2wareki }})</label>
</nz-form-control>
</div>
</div>
</nz-form-item>
<div>
<validation-messages
minHeight
[control]="detailForm.controls['birthday']"
[fieldName]="'CustomerDetail.form.birthday.title'"
>
</validation-messages>
</div>
<!-- tel -->
<nz-form-item class="border">
<div class="form-title required">
{{ 'CustomerDetail.form.telNo.title' | translate }}
</div>
<div class="form-subtitle"></div>
<div class="form-content">
<div class="form-content-item">
<nz-form-label nzFor="telNo">{{
'CustomerDetail.form.telNo.subTitle' | translate
}}</nz-form-label>
<nz-form-control>
<input
nz-input
type="tel"
formControlName="telNo"
id="telNo"
maxlength="13"
appTelNoMask
[updateMode]="'blur'"
/>
</nz-form-control>
</div>
</div>
</nz-form-item>
<div>
<validation-messages
minHeight
[control]="detailForm.controls['telNo']"
[fieldName]="'CustomerDetail.form.telNo.title'"
>
</validation-messages>
</div>
<!-- address -->
<nz-form-item class="border">
<div class="form-title">
{{ 'CustomerDetail.form.address.title' | translate }}
</div>
<div class="form-subtitle">
<div class="form-subtitle-item">
<nz-form-label [nzNoColon]="true" nzFor="nameKanji">{{
'CustomerDetail.form.address.zipTitle' | translate
}}</nz-form-label>
</div>
<div class="form-subtitle-item">
<nz-form-label [nzNoColon]="true" nzFor="nameKana">{{
'CustomerDetail.form.address.addressTitle' | translate
}}</nz-form-label>
</div>
</div>
<div class="form-content">
<div class="form-content-item">
<nz-form-label nzFor="addressZip">{{
'CustomerDetail.form.halfWidthAlphanumeric' | translate
}}</nz-form-label>
<nz-form-control>
<input
nz-input
formControlName="addressZip"
type="tel"
maxlength="7"
/>
</nz-form-control>
</div>
<div class="form-content-item">
<nz-form-label nzFor="address">{{
'CustomerDetail.form.fullWidth' | translate
}}</nz-form-label>
<nz-form-control>
<input
nz-input
formControlName="address"
id="address"
placeholder="{{
'CustomerDetail.form.address.addressPlaceholder' | translate
}}"
/>
</nz-form-control>
</div>
</div>
</nz-form-item>
<div>
<validation-messages
minHeight
[control]="detailForm.controls['addressZip']"
[fieldName]="'CustomerDetail.form.address.zipTitle'"
>
</validation-messages>
</div>
<!-- email -->
<nz-form-item class="border">
<div class="form-title">
{{ 'CustomerDetail.form.email.title' | translate }}
</div>
<div class="form-subtitle"></div>
<div class="form-content">
<div class="form-content-item">
<nz-form-label nzFor="email">{{
'CustomerDetail.form.email.subTitle' | translate
}}</nz-form-label>
<nz-form-control>
<input
nz-input
formControlName="email"
id="email"
placeholder="{{
'CustomerDetail.form.email.placeholder' | translate
}}"
/>
</nz-form-control>
</div>
</div>
</nz-form-item>
<div>
<validation-messages minHeight [control]="detailForm.controls['email']">
</validation-messages>
</div>
<!-- point -->
<nz-form-item class="border">
<div class="form-title required">
{{ 'CustomerDetail.form.point.title' | translate }}
</div>
<div class="form-subtitle"></div>
<div class="form-content">
<div class="form-content-item">
<nz-form-label nzFor="point" class="label-multiple-line">
{{ 'CustomerDetail.form.halfWidthNumeral' | translate }}
</nz-form-label>
<nz-form-control>
<input
nz-input
formControlName="point"
id="point"
maxlength="9"
placeholder="{{
'CustomerDetail.form.point.placeholder' | translate
}}"
/>
</nz-form-control>
</div>
</div>
</nz-form-item>
<!--family-->
<nz-form-item class="border">
<div class="form-title">
{{ 'CustomerDetail.form.family.title' | translate }}
</div>
<div class="form-subtitle"></div>
<div class="form-content" formArrayName="family">
<div
*ngFor="let group of familyArray.controls; let i = index"
[formGroupName]="i"
>
<nz-form-item>
<label class="family-table-label"
>{{ 'CustomerDetail.form.family.name' | translate }} {{ i + 1 }}
</label>
<input
class="ant-input family-table-input"
formControlName="name"
/>
<small id="fnHelp" *ngIf="familyArray.invalid && familyArray.touched" class="form-text custom-invalid">error</small>
<label class="family-table-label"
>{{ 'CustomerDetail.form.family.relationship' | translate }}
{{ i + 1 }}
</label>
<select
class="ant-input family-table-input"
formControlName="relationship"
>
<option *ngFor="let item of relationships" [value]="item">
{{ item }}
</option>
</select>
<i
nz-icon
nzType="minus-circle-o"
class="dynamic-delete-button"
(click)="deleteFamilyInfo(i)"
></i>
</nz-form-item>
</div>
<div>
<button nz-button class="add-button" (click)="addFamilyInfo()">
<i nz-icon nzType="plus"></i>
家族追加
</button>
</div>
</div>
</nz-form-item>
<!-- actions [disabled]="detailForm.invalid"-->
<nz-form-item>
<nz-form-control [nzSpan]="24" class="actions">
<button nz-button nzType="primary" (click)="navigateBack()">
{{ 'CustomerDetail.actions.cancel' | translate }}
</button>
<button nz-button nzType="primary" (click)="submitForm()">
{{ 'CustomerDetail.actions.save' | translate }}
</button>
<button
*ngIf="routeParam.customerNo"
nz-button
nzType="primary"
(click)="navigateToFacilityPage()"
>
{{ 'CustomerDetail.actions.next' | translate }}
</button>
</nz-form-control>
</nz-form-item>
</form>
</nz-spin>
</nz-card>