1
votes

BACKGROUND:

I am trying my hand at an angular application and I'm having a huge issue with wrapping my head around routing.

I have 2 questions that I need to figure out that I have been struggling with for a few days now and at this point, I am just wasting time going in circles.

DETAILS:

I have a routes tree defined in my root app module like this:

export const ROUTES: Routes = [  
  { 
    path: "callback",  
    component: CallbackComponent
  },
  {
    path: "secure",
    loadChildren: "./secure/secure.module#SecureModule",
    canActivate: [AuthGuard]
  },  
  {
    path: "public",
    loadChildren: "./public/public.module#PublicModule"
  }, 
  {
    path: "",
    redirectTo: "public",    
    pathMatch: "full"
  },
  { path: "**", component: PageNotFoundComponent }
];

@NgModule({
  declarations: [AppComponent, PageNotFoundComponent],  
  imports: [    
    AuthModule,
    BrowserModule,
    BrowserAnimationsModule,    
    HttpClientModule,
    RouterModule.forRoot(ROUTES, {enableTracing: (environment.production === false)}),
    StoreModule.forRoot(reducers, { metaReducers }), //sets the entire app up to use ngrx store and applies the metaReducers class as a parent to all reducers used throughout the system. this helps with the debug tools
    EffectsModule.forRoot(effects),
    StoreRouterConnectingModule,
    environment.production === false ? StoreDevtoolsModule.instrument() : [],
  ],
  providers: [
    { provide: ErrorHandler, useClass: AppServices.RollbarErrorHandler },
    {
      provide: AppServices.RollbarService,
      useFactory: AppServices.rollbarFactory
    }
  ],
  exports: [AppComponent, PageNotFoundComponent]
})

When I first load my app with the URL localhost:4200, the public path gets loaded. That makes sense to me because the URL pattern matches the "" path so it gets redirected to the "public" path and loads that. This works fine as my public path gets loaded correctly.

The issue I am having comes up when I am authenticating against Auth0 which calls a callback URL defined as localhost:4200/callback after the user has been authenticated. I would think that this URL matches the "callback" route and should load the CallbackComponent. Unfortunately, it doesn't and it loads the public path again.

My CallbackComponent is defined in my AuthModule (where it also exported) which is obviously a separate module. In my experimenting, there is no issue with loading the component in the AppModule pages so I don't think this component being in another module is an issue but figured I'd mention it in case it is.

QUESTIONS

1) Why the heck is the "callback" path not loading when the URL is localhost:4200/callback?

2) Auth0 will pass token information and any errors back to that same URL in the querystring after a hash (eg localhost:4200/callback#error=... or, on success, localhost:4200/callback#access_token=...). Will that have an effect on the path matching? If so, what do I update my path to in order to handle that? If not, I believe that part of the URL will be considered a fragment, so I just get that information off the activatedRoute or is there some other way I am missing to handle fragments in routes?

Thanks in advance for any help.

UPDATE

I'm attaching the Router tracing blocks to show what exactly I'm seeing:

First is the navigation events when I navigate to the root URL

Navigated to http://localhost:4200/
platform-browser.js:380 Router Event: NavigationStart
platform-browser.js:367 NavigationStart(id: 1, url: '/')
platform-browser.js:367 NavigationStart {id: 1, url: "/"}
platform-browser.js:380 Router Event: RouteConfigLoadStart
platform-browser.js:367 RouteConfigLoadStart(path: public)
platform-browser.js:367 RouteConfigLoadStart {route: {…}}
core.js:3675 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
platform-browser.js:380 Router Event: RouteConfigLoadEnd
platform-browser.js:367 RouteConfigLoadEnd(path: public)
platform-browser.js:367 RouteConfigLoadEnd {route: {…}}
platform-browser.js:380 Router Event: RoutesRecognized
platform-browser.js:367 RoutesRecognized(id: 1, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 RoutesRecognized {id: 1, url: "/", urlAfterRedirects: "/public", state: RouterStateSnapshot}
platform-browser.js:380 Router Event: NavigationCancel
platform-browser.js:367 NavigationCancel(id: 1, url: '/')
platform-browser.js:367 NavigationCancel {id: 1, url: "/", reason: ""}
platform-browser.js:380 Router Event: NavigationStart
platform-browser.js:367 NavigationStart(id: 2, url: '/')
platform-browser.js:367 NavigationStart {id: 2, url: "/"}
platform-browser.js:380 Router Event: RoutesRecognized
platform-browser.js:367 RoutesRecognized(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 RoutesRecognized {id: 2, url: "/", urlAfterRedirects: "/public", state: RouterStateSnapshot}
platform-browser.js:380 Router Event: GuardsCheckStart
platform-browser.js:367 GuardsCheckStart(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 GuardsCheckStart {id: 2, url: "/", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
platform-browser.js:380 Router Event: ChildActivationStart
platform-browser.js:367 ChildActivationStart(path: '')
platform-browser.js:367 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationStart
platform-browser.js:367 ActivationStart(path: 'public')
platform-browser.js:367 ActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationStart
platform-browser.js:367 ChildActivationStart(path: 'public')
platform-browser.js:367 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationStart
platform-browser.js:367 ActivationStart(path: '')
platform-browser.js:367 ActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationStart
platform-browser.js:367 ChildActivationStart(path: '')
platform-browser.js:367 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationStart
platform-browser.js:367 ActivationStart(path: '')
platform-browser.js:367 ActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: GuardsCheckEnd
platform-browser.js:367 GuardsCheckEnd(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } , shouldActivate: true)
platform-browser.js:367 GuardsCheckEnd {id: 2, url: "/", urlAfterRedirects: UrlTree, state: RouterStateSnapshot, shouldActivate: true}
platform-browser.js:380 Router Event: ResolveStart
platform-browser.js:367 ResolveStart(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 ResolveStart {id: 2, url: "/", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
platform-browser.js:380 Router Event: ResolveEnd
platform-browser.js:367 ResolveEnd(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 ResolveEnd {id: 2, url: "/", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
platform-browser.js:380 Router Event: ActivationEnd
platform-browser.js:367 ActivationEnd(path: '')
platform-browser.js:367 ActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationEnd
platform-browser.js:367 ChildActivationEnd(path: '')
platform-browser.js:367 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationEnd
platform-browser.js:367 ActivationEnd(path: '')
platform-browser.js:367 ActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationEnd
platform-browser.js:367 ChildActivationEnd(path: 'public')
platform-browser.js:367 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationEnd
platform-browser.js:367 ActivationEnd(path: 'public')
platform-browser.js:367 ActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationEnd
platform-browser.js:367 ChildActivationEnd(path: '')
platform-browser.js:367 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: NavigationEnd
platform-browser.js:367 NavigationEnd(id: 2, url: '/', urlAfterRedirects: '/public')
platform-browser.js:367 NavigationEnd {id: 2, url: "/", urlAfterRedirects: "/public"}

This is the router tracing when I go straight to the callback url:

Navigated to http://localhost:4200/callback
platform-browser.js:380 Router Event: NavigationStart
platform-browser.js:367 NavigationStart(id: 1, url: '/')
platform-browser.js:367 NavigationStart {id: 1, url: "/"}
platform-browser.js:380 Router Event: RouteConfigLoadStart
platform-browser.js:367 RouteConfigLoadStart(path: public)
platform-browser.js:367 RouteConfigLoadStart {route: {…}}
core.js:3675 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
platform-browser.js:380 Router Event: RouteConfigLoadEnd
platform-browser.js:367 RouteConfigLoadEnd(path: public)
platform-browser.js:367 RouteConfigLoadEnd {route: {…}}
platform-browser.js:380 Router Event: RoutesRecognized
platform-browser.js:367 RoutesRecognized(id: 1, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 RoutesRecognized {id: 1, url: "/", urlAfterRedirects: "/public", state: RouterStateSnapshot}
platform-browser.js:380 Router Event: NavigationCancel
platform-browser.js:367 NavigationCancel(id: 1, url: '/')
platform-browser.js:367 NavigationCancel {id: 1, url: "/", reason: ""}
platform-browser.js:380 Router Event: NavigationStart
platform-browser.js:367 NavigationStart(id: 2, url: '/')
platform-browser.js:367 NavigationStart {id: 2, url: "/"}
platform-browser.js:380 Router Event: RoutesRecognized
platform-browser.js:367 RoutesRecognized(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 RoutesRecognized {id: 2, url: "/", urlAfterRedirects: "/public", state: RouterStateSnapshot}
platform-browser.js:380 Router Event: GuardsCheckStart
platform-browser.js:367 GuardsCheckStart(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 GuardsCheckStart {id: 2, url: "/", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
platform-browser.js:380 Router Event: ChildActivationStart
platform-browser.js:367 ChildActivationStart(path: '')
platform-browser.js:367 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationStart
platform-browser.js:367 ActivationStart(path: 'public')
platform-browser.js:367 ActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationStart
platform-browser.js:367 ChildActivationStart(path: 'public')
platform-browser.js:367 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationStart
platform-browser.js:367 ActivationStart(path: '')
platform-browser.js:367 ActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationStart
platform-browser.js:367 ChildActivationStart(path: '')
platform-browser.js:367 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationStart
platform-browser.js:367 ActivationStart(path: '')
platform-browser.js:367 ActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: GuardsCheckEnd
platform-browser.js:367 GuardsCheckEnd(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } , shouldActivate: true)
platform-browser.js:367 GuardsCheckEnd {id: 2, url: "/", urlAfterRedirects: UrlTree, state: RouterStateSnapshot, shouldActivate: true}
platform-browser.js:380 Router Event: ResolveStart
platform-browser.js:367 ResolveStart(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 ResolveStart {id: 2, url: "/", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
platform-browser.js:380 Router Event: ResolveEnd
platform-browser.js:367 ResolveEnd(id: 2, url: '/', urlAfterRedirects: '/public', state: Route(url:'', path:'') { Route(url:'public', path:'public') { Route(url:'', path:'') { Route(url:'', path:'') }  }  } )
platform-browser.js:367 ResolveEnd {id: 2, url: "/", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
platform-browser.js:380 Router Event: ActivationEnd
platform-browser.js:367 ActivationEnd(path: '')
platform-browser.js:367 ActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationEnd
platform-browser.js:367 ChildActivationEnd(path: '')
platform-browser.js:367 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationEnd
platform-browser.js:367 ActivationEnd(path: '')
platform-browser.js:367 ActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationEnd
platform-browser.js:367 ChildActivationEnd(path: 'public')
platform-browser.js:367 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ActivationEnd
platform-browser.js:367 ActivationEnd(path: 'public')
platform-browser.js:367 ActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: ChildActivationEnd
platform-browser.js:367 ChildActivationEnd(path: '')
platform-browser.js:367 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:380 Router Event: NavigationEnd
platform-browser.js:367 NavigationEnd(id: 2, url: '/', urlAfterRedirects: '/public')
platform-browser.js:367 NavigationEnd {id: 2, url: "/", urlAfterRedirects: "/public"}

It's essentially exactly the same except for the initial Navigated to value.

2
could you post what error you are getting in browser dev tools? and the way you are going to the url? are you using routerlink or just typing in browser search bar? Also the paths are case sensitive.Vato
There is no error. I enabled tracing on the RouterModule and it says nothing about the callback route at all. It jumps straight to the empty default route. I have tried going to the route by just pasting the url in the browser bar but in actual usage, Auth0 will be calling the url with some hash values after the url that I need to parse to get user information for authentication.JakeHova
what happens when you type any other non legit route? basically does it go to page not found "**" path component?Vato
Actually even in those cases that I put a path that doesn't exist, it still gets redirected to the public route.JakeHova
could you try using ' ' istead of " "? everything else seems fine to me by far in code.Vato

2 Answers

1
votes

Only to extend the otherwise valid Vato's answer:

Your main app module should export itself and not its component like this:

import { whatever } from "anywhere";

@NgModule({
   declarations: [...],  
   imports: [...],
   providers: [...],
   exports: [...] // exporting no AppComponent here
})
export class AppModule {}  // exports self class after all
1
votes

From what I've seen couple of things might be the problem.

You should use ' ' instead of "". Make sure you have in index.html Also the order of paths matters. I think you got that right. I'm not sure how important it is, but you are exporting the ROUTES instead of routemodule. Try to create routemodule something looking like this:

const routes: Routes = [
  { path: 'callback', component: CallbackComponent },
  { path: 'secure', loadChildren: './public/public.module#SecureModule' },
  { path: 'public', loadChildren: './public/public.module#PublicModule' },
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: '**' , component: PageNotFoundComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

and import it in your app module. Im also not sure why you exporting appComponent, I though there should be appmodule there.

import { AppRoutingModule } from "./app-router.module";

@NgModule({
    declarations: [AppComponent, PageNotFoundComponent],  
    imports: [    
      AuthModule,
      BrowserModule,
      BrowserAnimationsModule,    
      HttpClientModule,
      AppRoutingModule,
      StoreModule.forRoot(reducers, { metaReducers }), //sets the entire app up to use ngrx store and applies the metaReducers class as a parent to all reducers used throughout the system. this helps with the debug tools
      EffectsModule.forRoot(effects),
      StoreRouterConnectingModule,
      environment.production === false ? StoreDevtoolsModule.instrument() : [],
    ],
    providers: [
      { provide: ErrorHandler, useClass: AppServices.RollbarErrorHandler },
      {
        provide: AppServices.RollbarService,
        useFactory: AppServices.rollbarFactory
      }
    ],
    exports: [AppComponent, PageNotFoundComponent]
  })

The problem is somewhere here

In both of you routerTracing you are getting

platform-browser.js:367 NavigationStart(id: 1, url: '/')
platform-browser.js:367 NavigationStart {id: 1, url: "/"}

In second one you should be getting

platform-browser.js:367 NavigationStart(id: 1, url: '/callback')
platform-browser.js:367 NavigationStart {id: 1, url: "/callback"}

For your second question I think you need to implement 'callback:tocken' in path. Check Angular main guide and how they implement ids for heroes. But that might be different for tockens.

That's All I got so far. Good luck! I'll try to help more if you post updates.