2

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?

1
  • 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... Commented Mar 22, 2017 at 19:39

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, I started playing around with scan right after posting the question. This has lead me to another question. In your example, interval() emits, but I cant figure out how to do this without an observable method like interval because all of my network request are made after subscribing.
You can use merge() before scan() where you'll be merging some other Observable (or maybe in your case Subject) that emits data that'll go to the scan() operator.
0

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

2 Comments

I wanted to take a look at stores like this, thanks, but its a bit of an extreme approach at my relatively simple quest.
Still concept is valid. If you approach your data as immutable and have only one place (in a service) where you change them (via creating new object reference), everything would work.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.