ghiscoding
ghiscoding

Reputation: 13204

How to watch a collection change in Angular 5 within a Service

I'm trying to watch for an array collection change inside an Angular Service and I can't seem to figure out on how to do that in Angular 5. Basically, I am looking for the same thing as $watchCollection that we used to have in AngularJS, which is dirty checking and I'm aware of that.

I want to detect changes from within a Service, so I don't think I can or should use @Input. Also the array that I want to watch for changes, is a property inside an object. The reference that I have inside the Service is this.columnDefinition.collection (which the collection is an array of objects). Basically if I push a new object to the collection array, I want to detect it and do another action.

I tried to use IterableDiffers and DoCheck but that doesn't seem to detect or trigger when the collection array changes.

I also tried using Observable.of on the array (with RxJS 5), but this gets executed only on the first time

const observer = Observable.of(this.columnDefinition.collection);
observer.subscribe((col) => console.log(col))

I thought I could use ofArrayChanges but that got removed from ES6 consideration, and so it also got removed from RxJS 5 and up

I'm also trying to do this without using Subject since I want the user to only manipulate the array. They shouldn't know there's an observer attach to it

If I compare to the Aurelia framework, I can do exactly that with the following

this.bindingEngine
      .collectionObserver(this.columnDefinition.collection)
      .subscribe((changes: { index: number, addedCount: number, removed: any[] }[]) => {
         console.log('changes ', changes);
         console.log('updated collection', this.columnDefinition.collection);
      });

and it works, there's nothing fancy here.

EDIT

Just to clarify, the question is more about, can this be done without any special functions or any changes to the array? I know that I can do it with a Subject and an next but I'm trying to avoid that (if possible). I want the user to still use regular array push, pop, slice, ... If Aurelia can do it, is that also doable in Angular? ...it used to with $watchCollection in AngularJS so is there an equivalent in Angular 5?

Upvotes: 0

Views: 1951

Answers (1)

lupa
lupa

Reputation: 527

Because Array is using reference

this.columnDefinition.collection.push(// something);

This will affect the data inside array of course, but the reference to that array in this case collection is not changed.
Therefor if you want others component that subscribe to your observable to notify the change, you should also change the reference itself.

For example:
Service:

private collections: Collection[];//the data that will be emit
private collectionsSubject: BehaviorSubject<Collection[]>;//The subject to emit data
collections$: Observable<Collection[]>;//The public observable that other components will subscribe to
constructor() {
  this.collections = [];
  this.collectionsSubject = new BehaviorSubject<Collection[]>(this.collections);

  this.collections$ = this.collectionsSubject.asObservable();
}


//When ever other place all to this service to add new collection emit new data with new reference

addCollection(collection: Collection) {
  this.collections.push(collection);
  //Here we will renew the reference and passing it to others component using spread operator
  this.fetchOptionsSubject.next([...this.collections]);
}

Component:

this.collectionService.collections$.subscribe((collections: Collection[]) => {
  this.collections = collections;
});

You could learn more about Spread syntax

Upvotes: 1

Related Questions