angularangular-routingangular-resolver

Angular route loading with async redirect


I need to wait for some API call before letting page in my route to load. Basically we have A/B testing enabled (which is API call) and based on that will have to either load Component assosiated with that route or redirect to different url. To do that I was trying to use resolver that returns Observable<boolean>. Here is code:

export class RedirectResolveService implements Resolve<Observable<boolean>> {
  private empty = new Observable<boolean>();
  constructor(private apiService: ApiService, private router: Router) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> {
    return this.apiService.get("/api/abtest/mypage").pipe(
      tap((isOn) => {
        if (!isOn) {
          window.location.href = "/welcome-page";
        }
      }),
      first()
    );
  }
}

Then I add this in the route:

{
  path: 'new-page',
  component: NewPageComponent,
  resolve: {
    redirect: RedirectResolveService,
  },
},

And then in NewPageComponent I add subscription in the constructor:

export class NewPageComponent {
  constructor(private route: ActivatedRoute) {
    this.route.data.subscribe();
  }
}

It works but for the case when redirect happens I see first NewPageComponent rendered and then redirect happening. That makes sense since I subscribe to my Observable when Component initializes and thus only start redirect after that.

Is there way to make redirect logic without Component being initialized?


Solution

  • Using route guard it would be something like this

    @Injectable({
      providedIn: "root",
    })
    export class RedirectGuard implements CanActivate {
      constructor(
        private _authService: AuthService,
        private _router: Router,
        private apiService: ApiService
      ) {}
    
      canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
      ): Observable<boolean | UrlTree>
        | Promise<boolean | UrlTree>
        | boolean
        | UrlTree {
        return this.apiService.get("/api/abtest/mypage").pipe(
          tap(isOn => isOn || this._router.parseUrl("/welcome-page")),
          first()
        );
      }
    }