Why observable result won't change while array push/splice

Viewed 733

I have a service like this:

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);
  }
}

and I subscribe the getItems() observable in my component for my further purpose:

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

All the action is directly call the function in ArrService. When I called the setValue(), the source array in ArrService and local array in component both changed. And I record that the obseravble didn't emit again while change, so I guess that the obserables should be pass by reference.

But when I called the addItem() and removeItem(), the source change, but the local didn't. So the obserable isn't pass by reference? Or I'm misunderstanding how obseravble work?

I know subject or behaviourSubject can complete my service. But I wanna know how the observable functional in this case, why some action is follow but some doesn't.

2 Answers

Try 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();
  }
}

Use BehaviorSubject if you want to retain the last emitted value (useful in cases when you are subscribing after you have emitted value using .next() )


When you work with arrays, there is always problem associated to monitor any changes that has been made in it. You'll encounter similar behavior in other angular places as well where if you insert any new value to an array, the "ngOnChange" or even "zone.js" wont detect any change in value . In such cases, either you create new array reference or handle it manually.

A subject is a type of observable. As such, it is a stream. You can add an item (such as a number) to the stream and the item will be broadcast to any observers of the stream.

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

Or if you need default value:

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

raise event in setValue this way:

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

change the getItems this way:

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

Final Result:

@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();
  }
}
Related