Tengo datos que estoy extrayendo de mi servidor en función de una fecha específica. Entonces, cuando cambia la fecha, vuelvo a consultar desde el servidor para obtener los nuevos datos. También tengo la capacidad de activar manualmente una actualización desde el servidor (para los casos en los que extraigo datos, por lo que necesito volver a extraer)

private _date = new BehaviorSubject(new Date())
private _refresh = new BehaviorSubject<void>(undefined); 
readonly data$: Observable<Model[]> = combineLatest([this._date, this._refresh])
   .pipe( 
      switchMap(([date, refresh]) => this.dataService.get(date)),
      shareReplay(1) 
   )

Mi vista muestra data$ usando una canalización asíncrona

Necesito poder manipular la lista en data$ para poder realizar un seguimiento de los elementos modificados en la lista.

Model {
   ...
   isModified: boolean
}

Mi modelo es así, así que si hago un cambio en un elemento, quiero poder insertarlo en la lista de datos (eliminar el elemento antiguo -> reemplazar con el nuevo elemento modificado) y luego hacer que data$ emita la lista de actualización. .

Entonces, si el _date emite $data lo emitiría y todo lo modificado sería eliminado.

Intentaba no modificar los elementos de la lista directamente para poder utilizar ChangeDetectionStrategy.OnPush

¿Hay alguna manera en la que pueda combinar el proceso de actualizar manualmente la lista y _date actualizar la lista, pero al extraer del servidor eso tendría prioridad?


Solo mantener una lista debería permitirme obtener agregados más fácilmente basados ​​en los datos. A medida que los artículos se actualizan, mis valores agregados se actualizarán en función de la lista modificada. Si mantuviera una segunda lista de elementos modificados, necesitaría comparar las dos listas si quisiera que los valores agregados estuvieran en el estado modificado (obtenga todos los elementos de la lista principal que no están modificados y todos los elementos de la lista modificada).

La actualización por lotes debería ser tan fácil como

data$.pipe(
   filter(x => x.isModified), 
   switchMap(x => dataService.update(x))
)

Aquí hay un stackblitz que creo que ayudará a demostrar.

Aquí puede ver que tengo un componente que permite actualizar el nombre de los elementos devueltos por mi observable. Me gustaría poder modificar esos elementos y luego almacenarlos nuevamente en el observable. De esta manera, si quisiera obtener todos los elementos modificados, podría aplicar un filtro en ese observable para obtener los elementos modificados. Además, si cambia la fecha, el observable volverá a extraerse del servidor y la lista de elementos modificados se eliminará.

Ver DataService.itemNameChanged

1
rpascal 29 jul. 2020 a las 19:34

1 respuesta

La mejor respuesta

Para resolver este problema, puede usar el operador de escaneo para mantener el valor de los nuevos elementos en la matriz.

Aquí se crea un BehaviorSubject para emitir los valores de cada nuevo elemento modificado, se inicializa con el primer valor de la matriz predeterminada.

  private itemModified = new BehaviorSubject<Model>(data[0]);

Cada vez que se actualizan los observables base para dateRefresh $, este valor debe restablecerse.

  readonly dateRefreshHttp$: Observable<Model[]> = combineLatest([
    this._date,
    this._refresh
  ]).pipe(
    tap(() => this.itemModified.next(data[0])),
    switchMap(([date, refresh]) =>

El operador de escaneo se agrega dentro de switchMap, para reiniciar la acumulación cada vez que se cambia. Tenga en cuenta el operador antirrebote agregado para permitir la entrada múltiple de caracteres sin perder el enfoque.

   switchMap(([date, refresh]) =>
      combineLatest([from([data]), this.itemModified]).pipe(
        scan(
          (acc: any[], [data, itemModified], index: number) => {
            return acc.map(item =>
              item.id == itemModified.id ? itemModified : item
            );
          },
          [...data]
        )
      )
    ),
    debounceTime(300)
  );

Por último, se emite un nuevo valor a través de la función de cambio de nombre del elemento.

  itemNameChanged(newName: string, item: Model) {
    const newItem: Model = {
      id: item.id,
      name: newName,
      isModified: true
    };

    this.itemModified.next(newItem);
  }

Ejemplo en vivo, Stackblitz

1
bernatsampera 30 jul. 2020 a las 13:15