2
votes

In my template I subscribe to an observable array like so:

<div *ngFor="let item of activities | async">...</div>

I changed my activities array to an observable array because I like the idea of the view subscribing and unsubscribing from my observables.

My question is, after initially retrieving the observables and mapping them into an activities object array, how do I append or prepend activities to that array without overwriting the entire observable array?

To be specific, I am creating an infinite scroll and upon the new http request completing, I have 12 new activity objects that I need to append to the activities observable array.

Activities definition:

public activities: Observable<Array<ActivitiesItemModel>>;

My initial request:

this.activities = this._transactionService.Get(url)
  .map(activities => {
    // Creates an activities model which contains items<ActivitiesItemModel>
    return new activitiesModel(activities).items;
  })

My infinite scroll request:

// This is where I am stuck..
// I assumed I could just map the values retrieved from the request
this.activities
  .map(a => {
    return this._transactionService.Get(queryUrl)
      .map(activities => {
        infiniteScroll.complete();

        return activities;
      })
  })

Any ideas?

3
You can (and should) overwrite existing observable array. If you have performance issues you can use ChangeDetectionStrategy.OnPush, which doesn't work if you mutate array, that's why it's better to overwrite it. Keep your downloaded data in one place and use filtered copy of this data for display... - Sasxa

3 Answers

2
votes

This looks like an ideal case for the scan() operator with an empty array as a default value.

This example buffers five values into an array and then merges them with the previous array so it'll continuously print an ever increasing array of values. This should be the same what you're trying to achieve with the infinite scroll:

Observable.interval(100)
  .bufferCount(5)
  .scan((acc, val) => {
    acc = acc.concat(val);
    return acc;
  }, [])
  .subscribe(val => console.log(val));

See live demo: https://jsbin.com/jilocaz/2/edit?js,console

[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
...

Of course you're going to use some remote service instead of Observable.interval() but I think you get the point.

0
votes

I used Redux store in one of my applications. Following link explains why it's good idea to do it that way and shows code examples of how to do it. Should match your case. On top - if you don't use this approach - watch out for mutations of data and its effect on change detection. Explained in second link.

http://onehungrymind.com/build-better-angular-2-application-redux-ngrx/

https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

0
votes

I think you could setup two sources and use concat something like this;

let source1 = Rx.Observable.return(getActivities);
let source2 = Rx.Observable.return(getMoreActivities);
let scrollevent$ = new BehaviourSubject();
source2.skipUntil( scrollevent$ ); // ignore until this creates a value.

then...

let source = Rx.Observable.concat(source1, source2)
    .subscribe( activities => ... );

onScroll() {
 scrollevent$.next(null);
}