ngModel 的更新值未反映在组件内部
Update value of ngModel is not reflected inside the component
我有一个具有 ControlValueAccessor
实现的组件,但是当绑定值在组件外部更改时,它不会正确更新。
编辑:
Stackblitz 示例:
https://stackblitz.com/edit/angular-lmojnj
我会解释senario。
一开始数组中有两个图像,如果我单击其中一个图像,它将触发图像排序器的 toggleSelected
,这将更新 tempImages 以只有 1 个图像(未单击的图像)。
然后,如果我在 parent.component 的输入中放入 url 图像并单击回车,它将触发 parent.component 的 addImage
,从而将 url 推送到 tempImages
因此应该在图像分类器中执行逻辑(应该自动调用 writeValue 导致值改变吗? - 它没有)。
现在,如果我再次点击我之前点击的同一张图片(或选择的另一张图片),我之前添加的新图片就会出现!
它应该在我将它添加到 tempImages
数组时出现,因为它是双向绑定,不是吗?
为了解决这个问题,我添加了对图像排序器的引用(<app-images-sorter2 #imagesSorter [(ngModel)]="tempImages"></app-images-sorter2>
)
然后将引用传递给 addImage 并调用 imageSorterCompoennt.writeValue(this.tempImages);
来更新我的预期,但据我所知这是不好的做法和错误的解决方案
图片-sorter2.component.ts:
import { Component, forwardRef, ViewEncapsulation } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
@Component({
selector: 'app-images-sorter2',
templateUrl: './images-sorter2.component.html',
styleUrls: ['./images-sorter2.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ImagesSorter2Component),
multi: true
}
],
encapsulation: ViewEncapsulation.None
})
export class ImagesSorter2Component implements ControlValueAccessor {
public allImages: string[] = [];
public images: string[] = [];
private onChange: (_: any) => void = () => {};
constructor() {}
public writeValue(value: string[]): void {
this.images = value || [];
this.images.forEach((image) => {
if (!this.allImages.includes(image)) {
this.allImages.push(image);
}
});
this.onChange(this.images);
}
public registerOnChange(fn: (_: any) => void): void {
this.onChange = fn;
}
public registerOnTouched(): void {}
public isSelected(image: string): boolean {
return this.images.includes(image);
}
public getImageModelIndex(image: string): number {
return this.images.indexOf(image);
}
public toggleSelected(image: string): void {
const indexOfImage = this.getImageModelIndex(image);
let newValue;
if (indexOfImage > -1) {
newValue = [...this.images.slice(0, indexOfImage), ...this.images.slice(indexOfImage + 1)];
} else {
newValue = [...this.images, image];
}
this.writeValue(newValue);
}
}
图片-sorter2.component.html:
<div fxLayout="row wrap">
<div *ngFor="let image of allImages"
class="image-wrapper"
[ngClass]="{'selected': isSelected(image)}"
(click)="toggleSelected(image)">
<div class="index-container">
<div>{{ getImageModelIndex(image)+1 }}</div>
</div>
<div class="image-container">
<img [src]="image" />
</div>
</div>
</div>
parent.component.ts:
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ParentComponent {
public tempImages = [
'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Google_Chrome_icon_%28September_2014%29.svg/1200px-Google_Chrome_icon_%28September_2014%29.svg.png',
'https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/1200px-Apple_logo_black.svg.png'
];
public newImage: string = '';
public addImage(): void {
this.tempImages.push(this.newImage);
this.newImage = '';
}
}
parent.component.html:
<app-images-sorter2 [(ngModel)]="tempImages"></app-images-sorter2>
<div>
<mat-form-field>
<input matInput
type="text"
[(ngModel)]="newImage"
(keyup.enter)="addImage()"
placeholder="Image URL">
</mat-form-field>
</div>
如 stckblitz 所示,string[]
上没有函数 includes
。
根据docs it should work, but after replacing it to .indexOf(image) === -1
it works. Example
我之前遇到过这个问题,似乎 ControlValueAccessor 在注意到集合更改方面存在问题,因此您必须重新分配数组。
对于您的示例,将 parent.ts 文件中的第 18 行更改为
this.tempImages.push(this.newImage);
至
this.tempImages = this.tempImages.concat([this.newImage]);
它应该可以正常工作。
我有一个具有 ControlValueAccessor
实现的组件,但是当绑定值在组件外部更改时,它不会正确更新。
编辑:
Stackblitz 示例:
https://stackblitz.com/edit/angular-lmojnj
我会解释senario。
一开始数组中有两个图像,如果我单击其中一个图像,它将触发图像排序器的 toggleSelected
,这将更新 tempImages 以只有 1 个图像(未单击的图像)。
然后,如果我在 parent.component 的输入中放入 url 图像并单击回车,它将触发 parent.component 的 addImage
,从而将 url 推送到 tempImages
因此应该在图像分类器中执行逻辑(应该自动调用 writeValue 导致值改变吗? - 它没有)。
现在,如果我再次点击我之前点击的同一张图片(或选择的另一张图片),我之前添加的新图片就会出现!
它应该在我将它添加到 tempImages
数组时出现,因为它是双向绑定,不是吗?
为了解决这个问题,我添加了对图像排序器的引用(<app-images-sorter2 #imagesSorter [(ngModel)]="tempImages"></app-images-sorter2>
)
然后将引用传递给 addImage 并调用 imageSorterCompoennt.writeValue(this.tempImages);
来更新我的预期,但据我所知这是不好的做法和错误的解决方案
图片-sorter2.component.ts:
import { Component, forwardRef, ViewEncapsulation } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
@Component({
selector: 'app-images-sorter2',
templateUrl: './images-sorter2.component.html',
styleUrls: ['./images-sorter2.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ImagesSorter2Component),
multi: true
}
],
encapsulation: ViewEncapsulation.None
})
export class ImagesSorter2Component implements ControlValueAccessor {
public allImages: string[] = [];
public images: string[] = [];
private onChange: (_: any) => void = () => {};
constructor() {}
public writeValue(value: string[]): void {
this.images = value || [];
this.images.forEach((image) => {
if (!this.allImages.includes(image)) {
this.allImages.push(image);
}
});
this.onChange(this.images);
}
public registerOnChange(fn: (_: any) => void): void {
this.onChange = fn;
}
public registerOnTouched(): void {}
public isSelected(image: string): boolean {
return this.images.includes(image);
}
public getImageModelIndex(image: string): number {
return this.images.indexOf(image);
}
public toggleSelected(image: string): void {
const indexOfImage = this.getImageModelIndex(image);
let newValue;
if (indexOfImage > -1) {
newValue = [...this.images.slice(0, indexOfImage), ...this.images.slice(indexOfImage + 1)];
} else {
newValue = [...this.images, image];
}
this.writeValue(newValue);
}
}
图片-sorter2.component.html:
<div fxLayout="row wrap">
<div *ngFor="let image of allImages"
class="image-wrapper"
[ngClass]="{'selected': isSelected(image)}"
(click)="toggleSelected(image)">
<div class="index-container">
<div>{{ getImageModelIndex(image)+1 }}</div>
</div>
<div class="image-container">
<img [src]="image" />
</div>
</div>
</div>
parent.component.ts:
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ParentComponent {
public tempImages = [
'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Google_Chrome_icon_%28September_2014%29.svg/1200px-Google_Chrome_icon_%28September_2014%29.svg.png',
'https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/1200px-Apple_logo_black.svg.png'
];
public newImage: string = '';
public addImage(): void {
this.tempImages.push(this.newImage);
this.newImage = '';
}
}
parent.component.html:
<app-images-sorter2 [(ngModel)]="tempImages"></app-images-sorter2>
<div>
<mat-form-field>
<input matInput
type="text"
[(ngModel)]="newImage"
(keyup.enter)="addImage()"
placeholder="Image URL">
</mat-form-field>
</div>
如 stckblitz 所示,string[]
上没有函数 includes
。
根据docs it should work, but after replacing it to .indexOf(image) === -1
it works. Example
我之前遇到过这个问题,似乎 ControlValueAccessor 在注意到集合更改方面存在问题,因此您必须重新分配数组。
对于您的示例,将 parent.ts 文件中的第 18 行更改为
this.tempImages.push(this.newImage);
至
this.tempImages = this.tempImages.concat([this.newImage]);
它应该可以正常工作。