Angular 属性 网格的反应形式
Angular Reactive Forms for property grid
使用 Angular 12,我想验证和检测列表中的更改。
我有一个 属性 网格(table 个 key/value 对,列出具有 editable 值的属性,但每个值可以是不同的类型, string/boolean/int/etc.).
我想为那个 属性 网格添加验证和更改检测。
应该对每个项目进行验证,并且只需要对整个列表进行更改检测(不关心更改了哪个 row/item)。
我构建了这样的东西:
export class InnerSetting {
key!: string;
displayName!: string;
originalValue?: string;
value?: string;
type!: PropertyTypeEnum; //string | int | boolean | ...
validation?: string;
minValue?: number;
maxValue?: number;
isNullable!: boolean
}
constructor(private formBuilder: FormBuilder) {
this.form = this.formBuilder.group({
properties: new FormArray([])
});
}
ngOnInit(): void {
this.settings.forEach(item => this.formArray.push(this.formBuilder.group(
{
key: [item.key],
type: [item.type],
name: [item.displayName],
value: [ item.value, [Validators.required, Validators.minLength(2)] ], //Depends on type and validation stuff, will be added later.
originalValue: [ item.originalValue ]
}
)));
//Not sure which of these will work, so added both for now.
this.form.valueChanges.pipe(debounceTime(500)).subscribe(changes => {
this.areChangesDetected = changes != null;
});
this.formArray.valueChanges.pipe(debounceTime(500)).subscribe(changes => {
this.areChangesDetected = changes != null;
});
}
get formArray() {
return this.form.controls.properties as FormArray;
}
在使用 Form
之前,我只是使用 InnerSetting
列表,所以请记住,我只是开始用表单替换列表。
setting
属性 是一个 InnerSetting
对象。
<form [formGroup]="form" class="group-wrapper">
<div class="d-flex item-row" *ngFor="let setting of formArray.controls; let i = index">
<div class="item flex-fill d-flex" [ngSwitch]="setting.type" [formGroupName]="i">
<span>{{setting.name}}</span>
<select *ngSwitchCase="'boolean'" class="flex-grow-1" name="role" id="role-select" [(ngModel)]="setting.value">
<option [value]="'0'">False</option>
<option [value]="'1'">True</option>
</select>
<div contenteditable *ngSwitchDefault class="flex-grow-1" type="text" [id]="setting.key" [innerText]="setting.value"></div>
</div>
<button class="remove-relation" (click)="reset(setting)">
<fa-icon [icon]="faUndo"></fa-icon>
</button>
</div>
</form>
问题
因为我需要根据设置类型(布尔值、字符串、数字等)显示不同的元素。如何从 formArray.controls
?
访问该信息
此外,我如何绑定到非标准输入控件,例如我的 div
和 contenteditable
?
编辑
我注意到 formArray.controls
是 FormGroup
的数组,我可以访问 this.formArray.controls[0].controls['name'].value
.
中的值
现在的问题是根据类型将该控件设置为字段(select 或 div 或输入)。
您的模板将如下所示
<form [formGroup]="form" class="group-wrapper">
<div
formArrayName="properties"
class="d-flex item-row"
*ngFor="let setting of settingFG; let i = index"
>
<div
class="item flex-fill d-flex"
[ngSwitch]="setting.get('type').value"
[formGroupName]="i"
>
<!-- <span>{{ setting.get('name').value }}</span> -->
<select
*ngSwitchCase="'boolean'"
class="flex-grow-1"
name="role"
id="role-select"
formControlName="value"
>
...
</select>
// custom div form control
<div-control *ngSwitchDefault formControlName="value"></div-control>
</div>
<button class="remove-relation" (click)="reset(setting)">X</button>
</div>
</form>
获取 FormGroup 数组的辅助方法
get settingFG(): FormGroup[] {
return this.formArray.controls as FormGroup[];
}
要将 FormControl 添加到 div,我们需要实现 ControlValueAccessor
export const DIV_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DivController),
multi: true,
};
@Component({
selector: 'div-control',
providers: [DIV_VALUE_ACCESSOR],
template: `<div contenteditable #div (input)="changeText(div.innerText)" [textContent]="value"><div>`,
})
export class DivController implements ControlValueAccessor {
value = '';
disabled = false;
private onTouched!: Function;
private onChanged!: Function;
changeText(text: string) {
this.onTouched(); // <-- mark as touched
this.value = text;
this.onChanged(text); // <-- call function to let know of a change
}
writeValue(value: string): void {
this.value = value ?? '';
}
registerOnChange(fn: any): void {
this.onChanged = fn; // <-- save the function
}
registerOnTouched(fn: any): void {
this.onTouched = fn; // <-- save the function
}
setDisabledState(isDisabled: boolean) {
this.disabled = isDisabled;
}
}
*不要忘记在模块中添加声明数组。
使用 Angular 12,我想验证和检测列表中的更改。
我有一个 属性 网格(table 个 key/value 对,列出具有 editable 值的属性,但每个值可以是不同的类型, string/boolean/int/etc.).
我想为那个 属性 网格添加验证和更改检测。
应该对每个项目进行验证,并且只需要对整个列表进行更改检测(不关心更改了哪个 row/item)。
我构建了这样的东西:
export class InnerSetting {
key!: string;
displayName!: string;
originalValue?: string;
value?: string;
type!: PropertyTypeEnum; //string | int | boolean | ...
validation?: string;
minValue?: number;
maxValue?: number;
isNullable!: boolean
}
constructor(private formBuilder: FormBuilder) {
this.form = this.formBuilder.group({
properties: new FormArray([])
});
}
ngOnInit(): void {
this.settings.forEach(item => this.formArray.push(this.formBuilder.group(
{
key: [item.key],
type: [item.type],
name: [item.displayName],
value: [ item.value, [Validators.required, Validators.minLength(2)] ], //Depends on type and validation stuff, will be added later.
originalValue: [ item.originalValue ]
}
)));
//Not sure which of these will work, so added both for now.
this.form.valueChanges.pipe(debounceTime(500)).subscribe(changes => {
this.areChangesDetected = changes != null;
});
this.formArray.valueChanges.pipe(debounceTime(500)).subscribe(changes => {
this.areChangesDetected = changes != null;
});
}
get formArray() {
return this.form.controls.properties as FormArray;
}
在使用 Form
之前,我只是使用 InnerSetting
列表,所以请记住,我只是开始用表单替换列表。
setting
属性 是一个 InnerSetting
对象。
<form [formGroup]="form" class="group-wrapper">
<div class="d-flex item-row" *ngFor="let setting of formArray.controls; let i = index">
<div class="item flex-fill d-flex" [ngSwitch]="setting.type" [formGroupName]="i">
<span>{{setting.name}}</span>
<select *ngSwitchCase="'boolean'" class="flex-grow-1" name="role" id="role-select" [(ngModel)]="setting.value">
<option [value]="'0'">False</option>
<option [value]="'1'">True</option>
</select>
<div contenteditable *ngSwitchDefault class="flex-grow-1" type="text" [id]="setting.key" [innerText]="setting.value"></div>
</div>
<button class="remove-relation" (click)="reset(setting)">
<fa-icon [icon]="faUndo"></fa-icon>
</button>
</div>
</form>
问题
因为我需要根据设置类型(布尔值、字符串、数字等)显示不同的元素。如何从 formArray.controls
?
此外,我如何绑定到非标准输入控件,例如我的 div
和 contenteditable
?
编辑
我注意到 formArray.controls
是 FormGroup
的数组,我可以访问 this.formArray.controls[0].controls['name'].value
.
现在的问题是根据类型将该控件设置为字段(select 或 div 或输入)。
您的模板将如下所示
<form [formGroup]="form" class="group-wrapper">
<div
formArrayName="properties"
class="d-flex item-row"
*ngFor="let setting of settingFG; let i = index"
>
<div
class="item flex-fill d-flex"
[ngSwitch]="setting.get('type').value"
[formGroupName]="i"
>
<!-- <span>{{ setting.get('name').value }}</span> -->
<select
*ngSwitchCase="'boolean'"
class="flex-grow-1"
name="role"
id="role-select"
formControlName="value"
>
...
</select>
// custom div form control
<div-control *ngSwitchDefault formControlName="value"></div-control>
</div>
<button class="remove-relation" (click)="reset(setting)">X</button>
</div>
</form>
获取 FormGroup 数组的辅助方法
get settingFG(): FormGroup[] {
return this.formArray.controls as FormGroup[];
}
要将 FormControl 添加到 div,我们需要实现 ControlValueAccessor
export const DIV_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DivController),
multi: true,
};
@Component({
selector: 'div-control',
providers: [DIV_VALUE_ACCESSOR],
template: `<div contenteditable #div (input)="changeText(div.innerText)" [textContent]="value"><div>`,
})
export class DivController implements ControlValueAccessor {
value = '';
disabled = false;
private onTouched!: Function;
private onChanged!: Function;
changeText(text: string) {
this.onTouched(); // <-- mark as touched
this.value = text;
this.onChanged(text); // <-- call function to let know of a change
}
writeValue(value: string): void {
this.value = value ?? '';
}
registerOnChange(fn: any): void {
this.onChanged = fn; // <-- save the function
}
registerOnTouched(fn: any): void {
this.onTouched = fn; // <-- save the function
}
setDisabledState(isDisabled: boolean) {
this.disabled = isDisabled;
}
}
*不要忘记在模块中添加声明数组。