2

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.

Shashank Vivek
  • 16,888
  • 8
  • 62
  • 104
Suspended
  • 169
  • 2
  • 11
  • Observables work best with immutable data, but your `ArrService` **mutates** its internal `arr` array which makes things more difficult. – Dai Jan 04 '21 at 08:49
  • Did you try to use a behavior subject instead of the array, and then call `.next()` when you want to update the value? – rmjoia Jan 04 '21 at 08:50
  • rmjoia is right you need to use a subject to notify the observable when there are mutations ! – Marco Jan 04 '21 at 08:59
  • @Carl : Did you try the answer I provided – Shashank Vivek Jan 05 '21 at 04:27
  • @ShashankVivek Actually my question is when I pass the array through the observable, the setQty() work, but the addItem() doesn't. Isn't it pass by reference? I wanna know the principle of observable work with array. – Suspended Jan 05 '21 at 10:42
  • @CarlTin: 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. Refer: https://stackoverflow.com/questions/42962394/angular-2-how-to-detect-changes-in-an-array-input-property – Shashank Vivek Jan 06 '21 at 04:09

2 Answers2

3

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.

Shashank Vivek
  • 16,888
  • 8
  • 62
  • 104
1

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();
  }
}
Masoud Bimmar
  • 6,941
  • 4
  • 30
  • 33