Launch modal from a service right after app start

Hey guys,

I have a quite straight forward app-setup built from the ionic blank starter (Ionic 5). I want to show some onboarding-screens to the users if they start the app for the first time or haven’t provided some relevant information yet which is needed by the app.
I have a central service which checks on the user data and makes the decision if the onboarding is needed after app start or not. This service is injected in the app component, so it’s launched right at app start. It should then launch the modal for the onboarding.

Now I have a strange behavior. When the user must login, everything is fine with the onboarding. When there is no login necessary because there is a valid auth token stored in a cookie, I got the error message “Can’t bind to ‘ngIf’ since it isn’t a known property of ‘div’”. After some research I found out that this error message probably means that the module of the onboarding modal component hasn’t loaded yet. That would explain why it doesn’t show up in login case (modules have enough time to load). I’m using lazy loading with preloadingStrategy: PreloadAllModules, as it is the default.

So the question now: Is there a way to check/wait for a specific module to be loaded? I don’t know if it’s possible to just make that one module not lazy loaded, but it would somehow also feel wrong because in 99% of the times the onboarding screens are not shown, so the lazy loading definetly makes sense here.
Do you have other ideas / design patterns how to achieve what I want?
My current workaround options which don’t feel very well are setTimeout to wait some time before the modal is shown (won’t do that) and launching the modal not from the service but from the page which is shown after app start. That would be okay if there is no clean option available, but it somehow doesn’t feel right as the logic doesn’t belong there.

Regards,
Jonas

Services are not intended to do UI

So your feelings about where goes what r likely against architectural principles of angular

A way to go about it to have a service do the checking and rhen emit message using behaviorsubject to whatever component u feel should deal with ui

Or have that component in one its lifecycle hooks do the check

SetTimeout to solve these things is indeed a sign of an upcoming big learning

Hey Tommertom,

thanks for your hints!

I know that services are not intended to do UI in general, but the question is if launching a modal after the service has done all the logic to decide if it’s necessary to launch the modal or not is probably debatable.

The thing is that also none of my pages is “in charge” of that onboarding process, as it’s covering stuff which kind of belongs to most of the pages.

Having an onboarding after first app start / registration is a very common thing in a lot of apps. So what’s the best practice to implement that?

Regarding your idea with service has the logic and fires an event which the page subscribes on. The problem will be that the event is fired before the page is initialized. So I can also go with a variable which is set in the service which the page checks on initialization. That’s more or less what I’ve done right now as my second workaround.
If there’s no “cleaner” way, I’ll probably stick with it.

I would put the ui stuff then in app.component.ts

If u use BehaviorSubject then the last emitted value will be picked up by the consumer. It embraces asynchronous stuff so to me at least a much cleaner way to what u r trying to do now

2 Likes

Yes, you can probably make one module to not lazy loaded by creating simple strategy that we can use to specify which routes we want to pre-load up front.

To create this strategy,Angular provides an abstract class called PreloadingStrategy that you can implement.This class has one method you need to override, preload.

To provide some determination on use the data property on the route definition to inform our strategy like this.


{
path:' ',
load children:'../tab2/tabs2.module#Tab2PageModule',
data: {
preload:true
},
}

Now create a SimpleLoadingStrategy class to pre-load lazy routes that have this data attribute.


import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';

export class SimpleLoadingStrategy implements PreloadingStrategy {
  preload(route: Route, load: Function): Observable<any> {
    if (route.data && route.data.preload) {
      return load();
    }
    return of(null);
  }
}


Now go into app-routing.module.ts and pass in the SimpleLoadingStrategy in the options. Also, since the custom strategy is a service, pass it into the providers collection as well:

@NgModule({
    providers: [SimpleLoadingStrategy],
    imports: [
      RouterModule.forRoot(routes, { preloadingStrategy: SimpleLoadingStrategy })
    ],
    exports: [RouterModule]
})
1 Like

okay, sounds good. I’ll go that way, thanks!

Hey,

thanks for that detailed description! For my use case I’m not going to use it as acutally it makes sense to lazy load my onboarding screen modules, as they are not visited in most cases.
But I’ll bookmark your post as I could definetly come into a situation where I can make use of that! Thanks!

Cool. Make sure the modal or whatever components u want to show is part of app.module.ts

yeah, that trap I’ve been falling into enough already! :grin:

1 Like