I have a case where an application should return menu based on given context
.
Below are the sources of menuA
and menuB
.
// example of menu derived from given source menuA() { return [ { a: 'demo1', b: 'demo2', c: 'demo3' }, { e: 'demo4', f: 'demo5', g: 'demo6' } ]; } // example of menu fetched from different source eg: db async menuB() { const ret = []; const h = { a: 'demo1', b: 'demo2', c: 'demo3' }; ret.push(h); return await ret; }
Given my limited knowledge and experience on rxjs
, I was hoping something like the snippet below can accept a string
of menuA
or menuB
to return an observable
of the required menu
:
getMenu$(context: string) { return forkJoin( { menuA: of(this.menuA()), menuB: from(this.menuB()) } ).pipe( mergeMap((m: any) => { return m[context]; }) ) }
The above invokes warning as below:
Argument of type 'Observable<unknown>' is not assignable to parameter of type 'OperatorFunction<{ menuA: ({ a: string; b: string; c: string; e?: undefined; f?: undefined; g?: undefined; } | { e: string; f: string; g: string; a?: undefined; b?: undefined; c?: undefined; })[]; menuB: { a: string; b: string; c: string; }[]; }, unknown>'. Type 'Observable<unknown>' provides no match for the signature '(source: Observable<{ menuA: ({ a: string; b: string; c: string; e?: undefined; f?: undefined; g?: undefined; } | { e: string; f: string; g: string; a?: undefined; b?: undefined; c?: undefined; })[]; menuB: { a: string; b: string; c: string; }[]; }>): Observable<...>'.ts(2345) The 'this' context of type 'void' is not assignable to method's 'this' of type 'Observable<any>'.ts(2684)
Based on a comment below, I have also added the warning message when map is used to replace the mergeMap:
// modified getMenu$: getMenu$(context: string) { return forkJoin( { menuA: of(this.menuA()), menuB: from(this.menuB()) } ).pipe( map(m => m[context]) ) }
Warning message:
Argument of type 'Observable<unknown>' is not assignable to parameter of type 'OperatorFunction<{ menuA: ({ a: string; b: string; c: string; e?: undefined; f?: undefined; g?: undefined; } | { e: string; f: string; g: string; a?: undefined; b?: undefined; c?: undefined; })[]; menuB: { a: string; b: string; c: string; }[]; }, unknown>'. Type 'Observable<unknown>' provides no match for the signature '(source: Observable<{ menuA: ({ a: string; b: string; c: string; e?: undefined; f?: undefined; g?: undefined; } | { e: string; f: string; g: string; a?: undefined; b?: undefined; c?: undefined; })[]; menuB: { a: string; b: string; c: string; }[]; }>): Observable<...>'.ts(2345) The 'this' context of type 'void' is not assignable to method's 'this' of type 'Observable<unknown>'.ts(2684)
I am using angular 12
and rxjs "~6.6.0"
.
Advertisement
Answer
DEMO: https://stackblitz.com/edit/typescript-ktm2hz?file=index.ts
function menuA() { return [ { a: 'demo1', b: 'demo2', c: 'demo3' }, { e: 'demo4', f: 'demo5', g: 'demo6' } ]; } // example of menu fetched from different source eg: db function menuB() { return of([ { a: 'demo1', b: 'demo2', c: 'demo3' } ]).pipe(delay(500)); } function getMenu$(context: string) { return forkJoin(of(menuA()), menuB().pipe(first())).pipe( map(([menuA, menuB]) => ({ menuA, menuB })), map(m => m[context]) ); } getMenu$('menuA').subscribe(data => { console.log(data); }); getMenu$('menuB').subscribe(data => { console.log(data); });
UPDATE:
you need to declare your menus. I have created Enum for it
DEMO with Angular 12: https://stackblitz.com/edit/angular-12-template-wcgohi?file=src/app/app.component.ts
import { Component, OnInit } from '@angular/core'; import { forkJoin, Observable, of } from 'rxjs'; import { first, map, delay } from 'rxjs/operators'; enum Menus { menuA = 'menuA', menuB = 'menuB' } @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { menuA(): { [key: string]: any }[] { return [ { a: 'demo1', b: 'demo2', c: 'demo3' }, { e: 'demo4', f: 'demo5', g: 'demo6' } ]; } // example of menu fetched from different source eg: db menuB(): Observable<{ [key: string]: any }[]> { return of([ { a: 'demo1', b: 'demo2', c: 'demo3' } ]).pipe(delay(500)); } getMenu$(context: Menus): Observable<{ [key: string]: any }[]> { return forkJoin([of(this.menuA()), this.menuB().pipe(first())]).pipe( map(([menuA, menuB]) => ({ menuA, menuB })), map(m => m[context]) ); } ngOnInit() { this.getMenu$(Menus.menuA).subscribe(data => { console.log(data); }); this.getMenu$(Menus.menuB).subscribe(data => { console.log(data); }); } }