Search code examples
rxjsangular11

Angular how to combine local function return value with runtime call back http request


I have local function to check some validation which returns true/false. I also have runtime callback function which is an async function ie. http call.

Note: This checkPermission function is happening inside a for loop.

I want to check if any othese two function call is true. Can anyone help me how to achieve this?

private checkPermissions(
        moduleId: number,
        permissions: number[],
        callback?: () => Observable<boolean>
    ): boolean {
        if(callback) {
            console.log('callback function defined');
        }

//following is the local function. how to make callback() here?

        return this.userSecurityService.userHasLicenseAndPermission(
            moduleId,
            permissions
        );
    }

My complete code is: Component:

options: NavOption[] = [];
this.options = this.sideNavService.loadMenus();

Sidenav service:

loadMenus(): NavOption[] {
    return this.getMenus();
}

private getMenus(): NavOption[] {
    const filteredMenuItems: NavOption[] = [];
    let menus =  [{
        id: 'recorded-events',
        label: 'Recorded events',
        icon: 'far fa-calendar-check fa-2x',
        url: `/incident/${this.organisationId}/list`,
        permissions: [
            EventReportingPermissions.View,
            EventReportingPermissions.ViewOwnEvents,
            EventReportingPermissions.ViewEmployeesEvents
        ],
        additionalPermissionCheck: () =>
            this.eventAccessGroupService.hasEventAccessGroupException()//this is the service to make http call
    },
    {
        id: 'new-events',
        label: 'Report new event',
        icon: 'far fa-calendar-plus fa-2x',
        url: `/incident/${this.organisationId}/create`,
        permissions: [EventReportingPermissions.Report]
    }]
    
    for(let item of menus) {
    
        let canAccess = this.checkPermissions(
                            topLevelItem.module,
                            subItem.permissions
                        );
        filteredMenuItems.push(item);
    }   
    return filteredMenuItems;
}

//local function

private checkPermissions(moduleId: number, permissions: number[]): boolean {
        //following returns value from local function and no http call
        return this.userSecurityService.userHasLicenseAndPermission(
            moduleId,
            permissions
        );
}

//additionalPermissionCheck?: () => Observable<boolean>;

Solution

  • I am not sure I am understanding correctly but is your callback the function that performs the permission checking?

    If so you can use a map pipe:

    // Beware this returns Observable<boolean> and not boolean
    const safeCallbackResult = callback ? callback() : of(true) // default to returning true as we'd like to check for the second condition
    return callback().pipe(
      map(canDoAction => canDoAction ? this.userSecurityService.userHasLicenseAndPermission(...) : false)
    )
    

    If you'd like to return a boolean, you can't. Because the moment you need to await for the callback's observable emission that is an operation that can take some time. Even though you could make the function async

    private async checkPermissions(
      moduleId: number,  
      permissions: number[],
      callback?: () => Observable<boolean>
    ): Promise<boolean> {
      // callback().toPromise() if using RxJS 6
      // firstValueFrom(callback()) if using RxJS 7
      if(callback && ! (await callback().toPromise())) return false
      return this.userSecurityService.userHasLicenseAndPermission(...)
    }