Búsqueda de sitios web

javascript - Por qué el resultado observable no cambia al hacer push/splice en un array


Tengo un servicio así:

export class ArrService {
  private arr: any[] = [1,2,3];

  constructor() {}

  setValue(index: number, value: number): void {
    this.arr[index]= value;
  }

  addItem(value: any): void {
    this.arr.push(element);
  }

  removeItem(index: number): void {
    this.arr.splice(index, 1);
  }

  getItems(): Observable<any> {
    return of(this.arr);
  }
}

y me suscribo al observable getItems() en mi componente para mi propósito posterior:

this.arrService.getItems().subscribe(res => {
  this._arr = res;
});

Toda la acción llama directamente a la función en ArrService. Cuando llamé a setValue(), tanto el arreglo fuente en ArrService como el arreglo local en el componente cambiaron. Y registré que el observable no emitió nuevamente durante el cambio, así que supongo que los observables deberían pasarse por referencia.

Pero cuando llamé a addItem() y removeItem(), la fuente cambió, pero la local no. ¿Entonces el observable no se pasa por referencia? ¿O estoy entendiendo mal cómo funciona el observable?

Sé que el sujeto o sujeto de comportamiento puede completar mi servicio. Pero quiero saber cómo funciona el observable en este caso, por qué algunas acciones se siguen pero otras no.

Respuesta #1

Prueba Subject

export class ArrService {
  private arr: any[] = [1,2,3];
  private arr$ = new Subject();

  constructor() {}

  setValue(index: number, value: number): void {
    this.arr[index]= value;
    this.arr$.next(this.arr);
  }

  addItem(value: any): void {
    this.arr.push(element);
    this.arr$.next(this.arr);
  }

  removeItem(index: number): void {
    this.arr.splice(index, 1);
    this.arr$.next(this.arr);
  }

  getItems(): Observable<any> {
    return this.arr$.asObservable();
  }
}

Usa BehaviorSubject si quieres conservar el último valor emitido (útil en casos cuando te estás suscribiendo después de haber emitido un valor usando .next())


Cuando trabajas con arreglos, siempre hay problemas asociados a monitorear cualquier cambio que se haya hecho en ellos. Te encontrarás con un comportamiento similar en otros lugares de Angular también, donde si insertas un nuevo valor en un arreglo, el "ngOnChange" o incluso "zone.js" no detectarán ningún cambio en el valor. En tales casos, o creas una nueva referencia de arreglo o lo manejas manualmente.

Respuesta #2

Un sujeto es un tipo de observable. Como tal, es un flujo. Puedes agregar un elemento (como un número) al flujo y el elemento será transmitido a cualquier observador del flujo.

private arr = new Subject<any[]>();

O si necesitas valor por defecto:

private arr = new BehaviorSubject<any[]>([1,2,3]);

disparar evento en setValue de esta manera:

  setValue(index: number, value: number): void {
    this.arr.next(value);
  }

cambia el getItems de esta manera:

  getItems(): Observable<any> {
    return this.arr.asObservable();
  }

Resultado final:

@Injectable()
export class ArrService {
  private arr = new BehaviorSubject<any[]>([1,2,3]);

  constructor() {}

  setValue(index: number, value: number): void {
    let tst = this.arr.value;
    tst.push(value);
    this.arr.next(tst);
  }


  getItems(): Observable<any> {
    // return of(this.arr);
    return this.arr.asObservable();
  }
}