Ero Stefano
Ero Stefano

Reputation: 646

Angular Router is slow

I am using Ionic with Angular under the hood.

Setup

"@angular/router": "~9.1.9",
"@ionic/angular": "^5.1.1",
"rxjs": "^6.5.5",

I use the following login method: When it's triggered the spinner is shown, data is loaded, routed to another page and then the spinner gets hid again.

loginWithGoogle() {
    this.showSpinner = true;
    this.authService.getAuthSessionPersistence()
        .pipe(
            concatMap(() => this.authService.getAuth().signInWithPopup(new auth.GoogleAuthProvider())),
            concatMap(() => this.databaseService.getUserModel()
                .pipe(
                    concatMap(userModel => {
                        if (Object.keys(new UserInit()).every(key => userModel[key])) {
                            return of(userModel).pipe(first());
                        }

                        return this.authService.getUser()
                            .pipe(
                                concatMap(user => this.databaseService.createUser(user.uid, userModel))
                            );
                    }),
                    concatMap(userModel => this.store.dispatch([
                            new SaveUserAction(userModel),
                            new LoadFixturesAction(),
                            new LoadTeamsAction(),
                            new LoadTransactionsAction(),
                        ])
                    ),
                    tap(() => this.router.navigateByUrl('tabs/home'))
                )),
            finalize(() => this.showSpinner = false)
        )
        .subscribe();
}

The problem is that the spinner is hid earlier than the routing has happened. The user sees for a short moment again the login screen which is not very user friendly.

As you see in the code above: The last RxJS operator is use finalize which is where the spinner gets hid.

Do you know why the router takes long to navigate?

Upvotes: 0

Views: 1529

Answers (3)

Ero Stefano
Ero Stefano

Reputation: 646

Thx to answers we managed to find a solution.

Mistakes

tap(() => this.router.navigateByUrl('tabs/home'))
  • this.router.navigateByUrl('tabs/home') returns a promise
  • tap does a side effect and doesnt wait for the promise to resolve

Solution

concatMap(() => from(this.router.navigateByUrl('tabs/home')))
  • from() converts the promise to an observable
  • concatMap() waits for the observable

Upvotes: 1

martin
martin

Reputation: 96959

I think you can concatMap into the chain another Observable with NavigationEnd event right before finalize().

...
concatMap(() => this.router.events.pipe(
  filter(event => event instanceof NavigationEnd),
)),
finalize(() => this.showSpinner = false),

This way the chain will be disposed (and finalize triggered) only after the next NavigationEnd event.

Upvotes: 1

Sergey Rudenko
Sergey Rudenko

Reputation: 9235

I am not too familiar with rxjs operators myself, but the router.navigateByUrl method that you have returns a promise according to: https://angular.io/api/router/Router#navigateByUrl

This means your code can hide spinner after such promise is resolved:

...

tap(() => this.router.navigateByUrl('tabs/home').then(()=>{
        this.showSpinner = false
    }) 
)

...

Upvotes: 1

Related Questions