Javascript 在角度传感器中更新零部件数据时出现问题
我需要帮助来解决下一个问题: 我有一个组件Javascript 在角度传感器中更新零部件数据时出现问题,javascript,angular,typescript,Javascript,Angular,Typescript,我需要帮助来解决下一个问题: 我有一个组件a,它订阅API服务并每5秒获取一次新数据: ngOnInit(): void { const obs = this.dataService.getData(); this.data$ = obs.pipe(repeatWhen(() => interval(5000))); } <pre>{{data[index]|json}}</pre> <input [formControl]=
a
,它订阅API服务并每5秒获取一次新数据:
ngOnInit(): void {
const obs = this.dataService.getData();
this.data$ = obs.pipe(repeatWhen(() => interval(5000)));
}
<pre>{{data[index]|json}}</pre>
<input [formControl]="control">
在模板中,我有下一个代码:
<div *ngFor="let data of data$ | async">
<app-view-data [data]="data"></app-view-data>
</div>
问题在于,当来自dataService
的可观察对象发出新值时,ViewDataComponent
组件将被重建,而输入的值将丢失
在大多数情况下,发出的数据包含相同的数据集,可以稍微更改,例如,计数
可以针对相同的项目进行更新
如果项目get被删除-丢失该输入是可以的,但是如果项目没有更改,或者只更新了计数-则输入必须保留。不应重建ViewDataComponent
组件
我知道angular-trackBy
中有一个优化功能,但这不适合我,因为数据可以更改(例如,计数可以更新),但项目必须保持不变。-在这种情况下,ViewDataComponent
应该只更新计数,而不进行任何其他视觉更改。trackBy
通常会对您有所帮助,因为它会阻止整个列表的重建。默认情况下,对象通过引用进行跟踪,但对象并不是您真正想要的。相反,您希望trackBy
返回对象相关信息的集合。这可以像id一样简单,但也更复杂
这将部分解决您的问题。如果刷新时没有更改,则不会重新生成该行。当有变化的时候,它就是
解决这一部分的一个想法是,通过将当前正在编辑的数据放入服务中来“持久化”您现在编辑的值,并将该值与刷新的值结合起来,然后重新填充相应的输入。不过,这对用户来说可能有点不稳定(可能需要重新聚焦等)。解决这一问题的一种方法是将您的注释保存在ViewDataComponent
的本地状态中,以便在重新渲染之间保留它们
示例:您正在尝试合并两个数组(原始数组有一些更改,例如inputValue),以及新数组,这可能会添加、删除或更新现有元素。我认为您应该利用rxjs并将两个数组合并成一个新数组-保存旧值并将它们添加到新到达的数据中:
ngOnInit(): void {
const obs = this.dataService.getData();
this.data$ = obs.pipe(
repeatWhen(() => interval(5000)),
map((v) => {
// merge the two arrays here
})
);
}
这是一个-你需要用按钮触发发射
您可以通过保留旧值来改进此解决方案(使合并函数更复杂,因为您必须更新所有属性更改),这将获得不重新渲染未更改的对象的好处,使UI更快\更平滑一些,但您可以使用此方法。这个想法不是迭代$data | async,而是一个FormArrayControls。我将创建一个FormGroups的FormArray,因此定义了一个函数,返回一个给出索引的FormGroup,另一个用于创建新的FormGroup
formArray=new FormArray([])
//get a formGroup from rormArray
getGroup(index)
{
return this.formArray.at(index) as FormGroup
}
//this allow create a new FormGroup
createGroup(id:number){
return new FormGroup({
id:new FormControl(id),
other:new FormControl()
})
}
在observable中,使用“tap”,我们将检查响应并更改formArray,添加不存在的元素,如果响应不包括formArray,则删除元素
this.data$ = interval(5000).pipe(
switchMap(_=>this.dataService.getData(this.count)), //<--really I want to return this
tap((res:any[])=>{
const idsArrays=this.formArray.value.map(x=>x.id)
const idsres=res.map(x=>x.id)
//search in formArray to add new elements
res.forEach(x=>{
if (idsArrays.indexOf(x.id)<0)
this.formArray.push(this.createGroup(x.id))
})
//check in res to remove elements of FormArray
for (let i=0;i<this.formArray.value.length;i++)
{
if (idsres.indexOf(this.formArray.value[i].id)<0)
{
this.formArray.removeAt(i);
i--;
}
}
this.count=(this.count+1)%5 //<--this is only to "simulate"
})
)
和.html,例如
{{data[index]| json}
你可以看到
注意,如果元素有多个“输入”,您可以使用FormGroup,并通过将您的输入从app view data
外部直接放入div?每个输入都与数据对象中的每个项目紧密关联。它不绑定到整个数据对象。在实际示例中,有更复杂的实现。输入只是一个例子。我试图创建一个本地状态,但出现了一个问题,组件被完全重新创建/重新初始化。我按照您的建议进行了操作。我将数据放入服务中。现在它起作用了。但我不认为这是一个好的解决方案,但至少它是有效的;谢谢你的建议。我会记住的。@Moshezauros,如果您使用*ngFor迭代更改的数组,则在更改时会丢失“焦点”(a*ngFor,每次更改数据时都会“清理”旧元素-删除模板-并重新绘制它们)。您的代码之所以有效,是因为您手动刷新了数据(使用按钮),这是正确的,但如果您已将新值合并到数组中(例如,仅更新计数值、删除删除的节点和添加新节点),则不会失去焦点
formArray=new FormArray([])
//get a formGroup from rormArray
getGroup(index)
{
return this.formArray.at(index) as FormGroup
}
//this allow create a new FormGroup
createGroup(id:number){
return new FormGroup({
id:new FormControl(id),
other:new FormControl()
})
}
control:FormControl;
index:number=0
id:number=0
data:any
@Input('data') set _data(value)
{
this.data=value;
if (this.control && this.control.value)
this.index=this.data.findIndex(x=>x.id==this.id)
};
@Input() set group(value){
this.id=value.value.id
this.control=value.get('other') as FormControl
this.index=this.data.findIndex(x=>x.id==this.id)
}
<pre>{{data[index]|json}}</pre>
<input [formControl]="control">