1

I'm pretty new to RxJS (using 5.1.1) and trying to understand it in my Angular 2 applciation using angular-redux/store.

I've got a store set up and working to get different parts of my state in my application.

Now, I'm trying to get AppParts that have the loaded property set to true:

my state:

export interface IAppState extends IRootState{
  theme: Theme;
  config: IConfig;
  mainSubTitle: string;
  appParts: AppPart[];
}

the select:

@select(state => state.app.appParts) appParts$: Observable<AppPart>;

now, I'm trying to get a filtered array from this observable:

loadedAppParts = [];

ngOnInit() {
  this.appParts$.filter(a => a.loaded).subscribe(a => { console.log(a); this.loadedAppParts.push(a); });
}

However, this returns an empty array. I'd like to be able to use the async pipe to get the 'loadedAppParts' if possible too, so I've also tried doing the following:

loadedAppParts: Observable<AppPart> = new Observable<AppPart>();
ngOnInit() {
  this.loadedAppParts = this.appParts$.filter(a => a.loaded);
}

So, how can I get a filtered array or observable from my Observable state?

Forgot to add my Rootstate:

export interface IRootState { };

and my initial state:

export const INITIAL_STATE: IAppState = {
  theme: THEMES.LightGreyBlue,
  config: <IConfig>{
    data: {
      apiUrl: ''
    },
    general: {
      appName: 'Default name',
      appShortName: 'default'
    }
  },
  mainSubTitle: 'Default',
  appParts: [new AppPart('core', true)]
};

And the template part that displays the JSON to debug (for the array example):

{{ appParts$ | async | json }} {{ loadedAppParts | json }}

When using the Observable: {{ appParts$ | async | json }} {{ loadedAppParts | async | json }}

This returns: [ { "name": "core", "loaded": true } ] null

5
  • Can you show the template part as well? This error happens when you try to convert two objects that reference to each other into JSON which I don't think is what you want to do. Commented Mar 6, 2017 at 8:05
  • when using the array: {{ appParts$ | async | json }} {{ loadedAppParts | json }} ; when using the Observable: {{ appParts$ | async | json }} {{ loadedAppParts | async | json }}; the last one returns [ { "name": "core", "loaded": true } ] null Commented Mar 6, 2017 at 8:14
  • From the output it looks like the appParts$ is emitting an array of AppPart objects so correct type would be Observable<AppPart[]>. If you try to flatten the emissions with this.loadedAppParts = this.appParts$.mergeAll().filter(a => a.loaded); does it help? Commented Mar 6, 2017 at 8:36
  • Hi Martin, great stuff, this indeed fixed it! Could you perhaps create an answer with a small explanation why mergeAll() has to be used here, I've never used that operator before. I'll accept it as an answer then! :) Thanks Commented Mar 6, 2017 at 8:42
  • 1 thing, this seems to return an object, not an array. So if there are 2 AppParts with the loaded property set to true, it only returns the last result. Can I get it so that loadedAppParts can be an array of AppParts? I guess the mergeAll() makes it an object instead of an array. Commented Mar 6, 2017 at 8:47

1 Answer 1

2

In the JSON output you can see it looks as follows:

[ { "name": "core", "loaded": true } ] null

So the appParts$ is in fact emitting arrays of objects (Observable<AppPart[]>) instead of just objects (Observable<AppPart>).

Then when you use this.appParts$.filter(a => a.loaded) you're trying to filter the items by .loaded property that doesn't exist on an Array object so it's always empty.

In fact you want to filter the objects inside that array. In other words you need to flatten the array into single items. This means we want to turn this:

[ { "name": "core", "loaded": true }, { "name": "core", "loaded": true }, { "name": "core", "loaded": true } ]

into this:

{ "name": "core", "loaded": true }
{ "name": "core", "loaded": true }
{ "name": "core", "loaded": true }

That's what the mergeAll() operator can do. Using mergeAll() is in this case the same as using merge(array => Observable.from(array)).

this.appParts$.mergeAll().filter(a => a.loaded);

Now when you chain it with .filter(a => a.loaded) you're filtering the AppPart objects.

Note that when using async filter it subscribes to the Observable and renders always only the last item emitted from the source.

You can use toArray() to again collect the filtered items into an array:

this.appParts$.mergeAll().filter(a => a.loaded).toArray();

This has one important consequence. The toArray() operator emits only after the source Observable has completed (but maybe this isn't an issue in your use-case).

Alternatively if you want to just collect all items you could use also scan() operator that emits the collection on every emission from the source (however this operator might cause multiple view updates).

this.appParts$.mergeAll().filter(a => a.loaded).scan((acc, item) => {
    acc.push(item);
    return acc;
}, []);
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! I've seemed to fix it with this code too: this.loadedAppParts = this.appParts$.map(appParts => appParts.filter(a => a.loaded)); this returns an Observable<AppPart[]> just like the appParts$.

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.