1
votes

I’m working on an Angular project that it is a web app with a landing page. The project is configured to show the landing when the base URL is hit, and to redirect to a login page when trying to access the app with no valid token in session.

The app is contained in its own module that does all the inner routing.

Everything works fine in this two scenarios:

ng serve
ng build
npx lite-server --baseDir="dist/frontend"

Then when building with production flag, routing breaks. These two following ways behave the same:

ng build --prod
npx lite-server --baseDir="dist/frontend"
ng build --prod
firebase deploy

When I try to reach anything inside the "client" module (http://host/client/anything), the app redirects me to the landing again (http://host/landing), no matter how, if pasting the URL in the browser bar or accessing through a control in the landing.

I've read Angular doc for deployment, but tips mentioned there are already configured in my case. Reading other similar stack post over there I could not find a solution.

What I tried so far:

  • using hash in routes: imports: [RouterModule.forRoot(routes, { useHash: true })]
  • disabling build optimizer and aot: ng build --prod --build-optimizer=false --aot=false
  • cheking the server config. index.html fallback is ok

Enabling tracing let me see that the router is in deed trying to reach the login page, but something that I don't get, breaks in the middle. Production build trace is:

main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Ee
main-es2015.4665a3d5fdcea262528d.js:1 NavigationStart(id: 5, url: '/client')
main-es2015.4665a3d5fdcea262528d.js:1 Ee {id: 5, url: "/client", navigationTrigger: "imperative", restoredState: null}id: 5navigationTrigger: "imperative"restoredState: nullurl: "/client"__proto__: we
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Re
main-es2015.4665a3d5fdcea262528d.js:1 RoutesRecognized(id: 5, url: '/client', urlAfterRedirects: '/client', state: Route(url:'', path:'') { Route(url:'client', path:'client') { Route(url:'', path:'') }  } )
main-es2015.4665a3d5fdcea262528d.js:1 Re {id: 5, url: "/client", urlAfterRedirects: "/client", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Oe
main-es2015.4665a3d5fdcea262528d.js:1 GuardsCheckStart(id: 5, url: '/client', urlAfterRedirects: '/client', state: Route(url:'', path:'') { Route(url:'client', path:'client') { Route(url:'', path:'') }  } )
main-es2015.4665a3d5fdcea262528d.js:1 Oe {id: 5, url: "/client", urlAfterRedirects: "/client", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: xe
main-es2015.4665a3d5fdcea262528d.js:1 ChildActivationStart(path: '')
main-es2015.4665a3d5fdcea262528d.js:1 xe {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Fe
main-es2015.4665a3d5fdcea262528d.js:1 ActivationStart(path: 'client')
main-es2015.4665a3d5fdcea262528d.js:1 Fe {snapshot: Tt}

main-es2015.4665a3d5fdcea262528d.js:1 Router Event: _e
main-es2015.4665a3d5fdcea262528d.js:1 NavigationCancel(id: 5, url: '/client')
main-es2015.4665a3d5fdcea262528d.js:1 _e {id: 5, url: "/client", reason: "Navigation ID 5 is not equal to the current navigation id 6"}

main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Ee
main-es2015.4665a3d5fdcea262528d.js:1 NavigationStart(id: 6, url: '/login?returnUrl=%2Fclient')
main-es2015.4665a3d5fdcea262528d.js:1 Ee {id: 6, url: "/login?returnUrl=%2Fclient", navigationTrigger: "imperative", restoredState: null}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Re
main-es2015.4665a3d5fdcea262528d.js:1 RoutesRecognized(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } )
main-es2015.4665a3d5fdcea262528d.js:1 Re {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Oe
main-es2015.4665a3d5fdcea262528d.js:1 GuardsCheckStart(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } )
main-es2015.4665a3d5fdcea262528d.js:1 Oe {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: xe
main-es2015.4665a3d5fdcea262528d.js:1 ChildActivationStart(path: '')
main-es2015.4665a3d5fdcea262528d.js:1 xe {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Fe
main-es2015.4665a3d5fdcea262528d.js:1 ActivationStart(path: 'login')
main-es2015.4665a3d5fdcea262528d.js:1 Fe {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: De
main-es2015.4665a3d5fdcea262528d.js:1 GuardsCheckEnd(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } , shouldActivate: true)
main-es2015.4665a3d5fdcea262528d.js:1 De {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: At, shouldActivate: true}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Pe
main-es2015.4665a3d5fdcea262528d.js:1 ResolveStart(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } )
main-es2015.4665a3d5fdcea262528d.js:1 Pe {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Te
main-es2015.4665a3d5fdcea262528d.js:1 ResolveEnd(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } )
main-es2015.4665a3d5fdcea262528d.js:1 Te {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: At}

main-es2015.4665a3d5fdcea262528d.js:1 Router Event: _e
main-es2015.4665a3d5fdcea262528d.js:1 NavigationCancel(id: 6, url: '/login?returnUrl=%2Fclient')
main-es2015.4665a3d5fdcea262528d.js:1 _e {id: 6, url: "/login?returnUrl=%2Fclient", reason: "Navigation ID 6 is not equal to the current navigation id 7"}

main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Ee
main-es2015.4665a3d5fdcea262528d.js:1 NavigationStart(id: 7, url: '/')
main-es2015.4665a3d5fdcea262528d.js:1 Ee {id: 7, url: "/", navigationTrigger: "imperative", restoredState: null}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Me
main-es2015.4665a3d5fdcea262528d.js:1 ActivationEnd(path: 'login')
main-es2015.4665a3d5fdcea262528d.js:1 Me {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Ne
main-es2015.4665a3d5fdcea262528d.js:1 ChildActivationEnd(path: '')
main-es2015.4665a3d5fdcea262528d.js:1 Ne {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Re
main-es2015.4665a3d5fdcea262528d.js:1 RoutesRecognized(id: 7, url: '/', urlAfterRedirects: '/landing', state: Route(url:'', path:'') { Route(url:'landing', path:'landing') } )
main-es2015.4665a3d5fdcea262528d.js:1 Re {id: 7, url: "/", urlAfterRedirects: "/landing", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Oe
main-es2015.4665a3d5fdcea262528d.js:1 GuardsCheckStart(id: 7, url: '/', urlAfterRedirects: '/landing', state: Route(url:'', path:'') { Route(url:'landing', path:'landing') } )
main-es2015.4665a3d5fdcea262528d.js:1 Oe {id: 7, url: "/", urlAfterRedirects: "/landing", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: xe
main-es2015.4665a3d5fdcea262528d.js:1 ChildActivationStart(path: '')
main-es2015.4665a3d5fdcea262528d.js:1 xe {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Fe
main-es2015.4665a3d5fdcea262528d.js:1 ActivationStart(path: 'landing')
main-es2015.4665a3d5fdcea262528d.js:1 Fe {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: De
main-es2015.4665a3d5fdcea262528d.js:1 GuardsCheckEnd(id: 7, url: '/', urlAfterRedirects: '/landing', state: Route(url:'', path:'') { Route(url:'landing', path:'landing') } , shouldActivate: true)
main-es2015.4665a3d5fdcea262528d.js:1 De {id: 7, url: "/", urlAfterRedirects: "/landing", state: At, shouldActivate: true}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Pe
main-es2015.4665a3d5fdcea262528d.js:1 ResolveStart(id: 7, url: '/', urlAfterRedirects: '/landing', state: Route(url:'', path:'') { Route(url:'landing', path:'landing') } )
main-es2015.4665a3d5fdcea262528d.js:1 Pe {id: 7, url: "/", urlAfterRedirects: "/landing", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Te
main-es2015.4665a3d5fdcea262528d.js:1 ResolveEnd(id: 7, url: '/', urlAfterRedirects: '/landing', state: Route(url:'', path:'') { Route(url:'landing', path:'landing') } )
main-es2015.4665a3d5fdcea262528d.js:1 Te {id: 7, url: "/", urlAfterRedirects: "/landing", state: At}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Me
main-es2015.4665a3d5fdcea262528d.js:1 ActivationEnd(path: 'landing')
main-es2015.4665a3d5fdcea262528d.js:1 Me {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Ne
main-es2015.4665a3d5fdcea262528d.js:1 ChildActivationEnd(path: '')
main-es2015.4665a3d5fdcea262528d.js:1 Ne {snapshot: Tt}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: be
main-es2015.4665a3d5fdcea262528d.js:1 NavigationEnd(id: 7, url: '/', urlAfterRedirects: '/landing')
main-es2015.4665a3d5fdcea262528d.js:1 be {id: 7, url: "/", urlAfterRedirects: "/landing"}
main-es2015.4665a3d5fdcea262528d.js:1 Router Event: Le
main-es2015.4665a3d5fdcea262528d.js:1 Scroll(anchor: 'null', position: 'null')
main-es2015.4665a3d5fdcea262528d.js:1 Le {routerEvent: be, position: null, anchor: null}

Navigation is cancelled twice. The first should be because of the auth-guard that returns false when trying to get the "client" module, but the reason seems to be another one according to what is logged.

The dev build trace is quite different:

platform-browser.js:96 Router Event: NavigationStart
platform-browser.js:87 NavigationStart(id: 5, url: '/client')
platform-browser.js:87 NavigationStart {id: 5, url: "/client", navigationTrigger: "imperative", restoredState: null}
​ Router Event: RoutesRecognized
​ RoutesRecognized(id: 5, url: '/client', urlAfterRedirects: '/client', state: Route(url:'', path:'') { Route(url:'client', path:'client') { Route(url:'', path:'') }  } )
​ RoutesRecognized {id: 5, url: "/client", urlAfterRedirects: "/client", state: RouterStateSnapshot}
​ Router Event: GuardsCheckStart
​ GuardsCheckStart(id: 5, url: '/client', urlAfterRedirects: '/client', state: Route(url:'', path:'') { Route(url:'client', path:'client') { Route(url:'', path:'') }  } )
​ GuardsCheckStart {id: 5, url: "/client", urlAfterRedirects: "/client", state: RouterStateSnapshot}
​ Router Event: ChildActivationStart
​ ChildActivationStart(path: '')
​ ChildActivationStart {snapshot: ActivatedRouteSnapshot}
​ Router Event: ActivationStart
​ ActivationStart(path: 'client')
​ ActivationStart {snapshot: ActivatedRouteSnapshot}
​ Router Event: NavigationCancel
​ NavigationCancel(id: 5, url: '/client')
​ NavigationCancel {id: 5, url: "/client", reason: "Navigation ID 5 is not equal to the current navigation id 6"}
​ Router Event: NavigationStart
​ NavigationStart(id: 6, url: '/login?returnUrl=%2Fclient')
​ NavigationStart {id: 6, url: "/login?returnUrl=%2Fclient", navigationTrigger: "imperative", restoredState: null}
​ Router Event: RoutesRecognized
​ RoutesRecognized(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } )
​ RoutesRecognized {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: RouterStateSnapshot}
​ Router Event: GuardsCheckStart
​ GuardsCheckStart(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } )
​ GuardsCheckStart {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: RouterStateSnapshot}
​ Router Event: ChildActivationStart
platform-browser.js:87 ChildActivationStart(path: '')
platform-browser.js:87 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:96 Router Event: ActivationStart
platform-browser.js:87 ActivationStart(path: 'login')
platform-browser.js:87 ActivationStart {snapshot: ActivatedRouteSnapshot}
platform-browser.js:96 Router Event: GuardsCheckEnd
platform-browser.js:87 GuardsCheckEnd(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } , shouldActivate: true)
platform-browser.js:87 GuardsCheckEnd {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: RouterStateSnapshot, shouldActivate: true}
platform-browser.js:96 Router Event: ResolveStart
platform-browser.js:87 ResolveStart(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } )
platform-browser.js:87 ResolveStart {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: RouterStateSnapshot}
platform-browser.js:96 Router Event: ResolveEnd
platform-browser.js:87 ResolveEnd(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient', state: Route(url:'', path:'') { Route(url:'login', path:'login') } )
platform-browser.js:87 ResolveEnd {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient", state: RouterStateSnapshot}
platform-browser.js:96 Router Event: ActivationEnd
platform-browser.js:87 ActivationEnd(path: 'login')
platform-browser.js:87 ActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:96 Router Event: ChildActivationEnd
platform-browser.js:87 ChildActivationEnd(path: '')
platform-browser.js:87 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
platform-browser.js:96 Router Event: NavigationEnd
platform-browser.js:87 NavigationEnd(id: 6, url: '/login?returnUrl=%2Fclient', urlAfterRedirects: '/login?returnUrl=%2Fclient')
platform-browser.js:87 NavigationEnd {id: 6, url: "/login?returnUrl=%2Fclient", urlAfterRedirects: "/login?returnUrl=%2Fclient"}
platform-browser.js:96 Router Event: Scroll

Any help is appreciated. Thanks in advance.

1

1 Answers

1
votes

If you use firebase hosting to host your build, you need to enable rewrites to index.html.

you can do this in your firebase.json file by adding the rewrites section:

{
  "hosting": {
    "public": "dist/browser",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    ...
  }
}