Angular 实现 ControlValueAccessor 的组件无法进行双向绑定
Angular component that implements ControlValueAccessor fails for two way binding
包装 material select 控件并实现 ControlValueAccessor 的自定义组件无法双向绑定,但成功地以一种方式绑定。
Component.ts
@Component({
selector: 'page-size-select',
templateUrl: 'page-size-select.component.html',
inputs: ['pageSize'],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: PageSizeSelectComponent,
multi: true
}]
})
export class PageSizeSelectComponent implements ControlValueAccessor{
pageSizes = [
{ id: 10, name: '10' },
{ id: 25, name: '25' },
{ id: 50, name: '50' },
{ id: -1, name: 'All' }
];
innerPageSize: number;
constructor() {}
private changed = new Array<(value: number) => void>();
private touched = new Array<() => void>();
get pageSize(): number {
return this.innerPageSize;
}
set pageSize(pageSize: number) {
if (this.innerPageSize !== pageSize) {
this.innerPageSize = pageSize;
this.changed.forEach(f => f(pageSize));
}
}
touch() {
this.touched.forEach(f => f());
}
writeValue(pageSize: number) {
this.innerPageSize = pageSize;
}
registerOnChange(fn: (value: number) => void) {
this.changed.push(fn);
}
registerOnTouched(fn: () => void) {
this.touched.push(fn);
}
}
组件模板
<mat-form-field>
<mat-select placeholder="Page Size..." [(ngModel)]="pageSize" name="pageSize" role="menu">
<mat-option *ngFor="let opt of pageSizes" [value]="opt.id" role="menuitem">
{{opt.name}}
</mat-option>
</mat-select>
</mat-form-field>
使用成功:
<page-size-select [pageSize]="pagination.pageSize" ></page-size-select>
使用失败:
<page-size-select ([pageSize])="pagination.pageSize" ></page-size-select>
当单向绑定成功时,它成功地将 pagination.pageSize
值读入基础 select 控件并将 selected 选项设置为该值。 objective 然而是双向绑定,但是当使用该语法时,绑定既不读取也不写入 pagination.pageSize
.
首先,你有一个语法错误:
([pageSize])="pagination.pageSize"
错了。方括号在外面,而不是圆括号:
[(pageSize)]="pagination.pageSize"
此外,我也不确定您的 ControlValueAccessor
实施是否正确。界面的目的是使组件与 Angular 表单控件一起工作。当您使用 ngModel
时,您正在使用 FormControl
而没有意识到它。所以 writeValue
应该调用已注册的更改函数 - 这就是组件的更改如何反映回已注册的 FormControl
,必须发生才能与 ngModel
写入绑定。你不能超过一个 FormControl
,所以你不需要 'manage multiple subscriptions'。同样,您需要调用已注册的触摸功能——通常是在组件模糊时。我做了一些与您正在做的非常相似的事情,我的 ControlValueAccessor
实现看起来像这样(几乎是 Angular 示例的剪切和粘贴):
writeValue(value: any) {
if (value !== this._value) {
this._value = value; // this is my internal value model
this._onChange(value);
this.change.emit(this._value); // I fire change events
}
}
_onChange: (value: any) => void = () => {};
registerOnChange(fn: (value: any) => void) {
this._onChange = fn;
}
_onTouched: () => any = () => {};
registerOnTouched(fn: any) {
this._onTouched = fn;
}
// if you implement disabled functionality you need this function
setDisabledState(value: boolean): void {
this._disabled = value; // this is my internal disabled model
}
// and my component's (blur) handler
onBlur() {
this._onTouched();
this.blur.emit(this._value); // I fire blur events
}
包装 material select 控件并实现 ControlValueAccessor 的自定义组件无法双向绑定,但成功地以一种方式绑定。
Component.ts
@Component({
selector: 'page-size-select',
templateUrl: 'page-size-select.component.html',
inputs: ['pageSize'],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: PageSizeSelectComponent,
multi: true
}]
})
export class PageSizeSelectComponent implements ControlValueAccessor{
pageSizes = [
{ id: 10, name: '10' },
{ id: 25, name: '25' },
{ id: 50, name: '50' },
{ id: -1, name: 'All' }
];
innerPageSize: number;
constructor() {}
private changed = new Array<(value: number) => void>();
private touched = new Array<() => void>();
get pageSize(): number {
return this.innerPageSize;
}
set pageSize(pageSize: number) {
if (this.innerPageSize !== pageSize) {
this.innerPageSize = pageSize;
this.changed.forEach(f => f(pageSize));
}
}
touch() {
this.touched.forEach(f => f());
}
writeValue(pageSize: number) {
this.innerPageSize = pageSize;
}
registerOnChange(fn: (value: number) => void) {
this.changed.push(fn);
}
registerOnTouched(fn: () => void) {
this.touched.push(fn);
}
}
组件模板
<mat-form-field>
<mat-select placeholder="Page Size..." [(ngModel)]="pageSize" name="pageSize" role="menu">
<mat-option *ngFor="let opt of pageSizes" [value]="opt.id" role="menuitem">
{{opt.name}}
</mat-option>
</mat-select>
</mat-form-field>
使用成功:
<page-size-select [pageSize]="pagination.pageSize" ></page-size-select>
使用失败:
<page-size-select ([pageSize])="pagination.pageSize" ></page-size-select>
当单向绑定成功时,它成功地将 pagination.pageSize
值读入基础 select 控件并将 selected 选项设置为该值。 objective 然而是双向绑定,但是当使用该语法时,绑定既不读取也不写入 pagination.pageSize
.
首先,你有一个语法错误:
([pageSize])="pagination.pageSize"
错了。方括号在外面,而不是圆括号:
[(pageSize)]="pagination.pageSize"
此外,我也不确定您的 ControlValueAccessor
实施是否正确。界面的目的是使组件与 Angular 表单控件一起工作。当您使用 ngModel
时,您正在使用 FormControl
而没有意识到它。所以 writeValue
应该调用已注册的更改函数 - 这就是组件的更改如何反映回已注册的 FormControl
,必须发生才能与 ngModel
写入绑定。你不能超过一个 FormControl
,所以你不需要 'manage multiple subscriptions'。同样,您需要调用已注册的触摸功能——通常是在组件模糊时。我做了一些与您正在做的非常相似的事情,我的 ControlValueAccessor
实现看起来像这样(几乎是 Angular 示例的剪切和粘贴):
writeValue(value: any) {
if (value !== this._value) {
this._value = value; // this is my internal value model
this._onChange(value);
this.change.emit(this._value); // I fire change events
}
}
_onChange: (value: any) => void = () => {};
registerOnChange(fn: (value: any) => void) {
this._onChange = fn;
}
_onTouched: () => any = () => {};
registerOnTouched(fn: any) {
this._onTouched = fn;
}
// if you implement disabled functionality you need this function
setDisabledState(value: boolean): void {
this._disabled = value; // this is my internal disabled model
}
// and my component's (blur) handler
onBlur() {
this._onTouched();
this.blur.emit(this._value); // I fire blur events
}