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">