I'm having issue with path param routing,
- where navigation works fine for any one of the category traversed first
- but routing doesn't work from one category to the other, even though the url in the browser updates.
- refreshing the browser loads the respective category.
- category to other pages works fine.
navigation.component.html
<nav >
<ul>
<li class="nav__item">
<a class="nav__link" [routerLink]="['/home']" routerLinkActive="active">home_link</a>
</li>
<li class="nav__item">
<a class="nav__link" [routerLink]="['/first']" routerLinkActive="active">first_link</a>
</li>
<li class="nav__item">
<a class="nav__link" [routerLink]="['/category/', '1']" routerLinkActive="active">link_1</a>
</li>
<li class="nav__item">
<a class="nav__link" [routerLink]="['/category/', '2']"
routerLinkActive="active">link_2</a>
</li>
<li class="nav__item">
<a class="nav__link" [routerLink]="['/category/', '3']"
routerLinkActive="active">link_3</a>
</li>
<li class="nav__item">
<a class="nav__link" [routerLink]="['/category', '4']"
routerLinkActive="active">link_4</a>
</li>
<li class="nav__item">
<a class="nav__link" [routerLink]="['/category', '5']"
routerLinkActive="active">link_5</a>
</li>
<li class="nav__item">
<a class="nav__link" [routerLink]="['/category', '6']"
routerLinkActive="active">link_6</a>
</li>
</ul>
</nav>
package.json
{
"name": "myApp",
"version": "0.1.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"postinstall": "ngcc",
"dev:ssr": "ng run myApp:serve-ssr",
"serve:ssr": "node dist/myApp/server/main.js",
"build:ssr": "ng build && ng run myApp:server",
"build-prod:ssr": "ng build --configuration production && ng run myApp:server",
"prerender": "ng run myApp:prerender"
},
"private": true,
"dependencies": {
"@angular/animations": "^13.3.11",
"@angular/cdk": "^13.3.9",
"@angular/common": "^13.3.11",
"@angular/compiler": "^13.3.11",
"@angular/core": "^13.3.11",
"@angular/forms": "^13.3.11",
"@angular/platform-browser": "^13.3.11",
"@angular/platform-browser-dynamic": "^13.3.11",
"@angular/platform-server": "^13.3.11",
"@angular/router": "^13.3.11",
"@babel/core": "^7.19.0",
"@fortawesome/angular-fontawesome": "^0.10.0",
"@fortawesome/fontawesome-svg-core": "^1.2.27",
"@fortawesome/free-brands-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@nguniversal/common": "^13.1.1",
"@nguniversal/express-engine": "^13.1.1",
"@nguniversal/module-map-ngfactory-loader": "^8.2.6",
"balanced-match": "^2.0.0",
"express": "^4.15.2",
"memory-cache": "^0.2.0",
"ngx-sharebuttons": "^10.0.0",
"ngx-spinner": "^13.1.1",
"rxjs": "^7.5.6",
"tslib": "^2.4.0",
"zone.js": "^0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^13.3.9",
"@angular/cli": "^13.3.9",
"@angular/compiler-cli": "^13.3.11",
"@ngtools/webpack": "^13.3.9",
"@nguniversal/builders": "^13.1.1",
"@types/express": "^4.17.13",
"@types/jasmine": "^3.6.0",
"@types/node": "^12.11.1",
"ajv": "^8.11.0",
"codelyzer": "^6.0.2",
"jasmine-core": "^3.6.0",
"jasmine-spec-reporter": "^5.0.0",
"karma": "^6.4.0",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.0.3",
"karma-jasmine": "^4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "^7.0.0",
"ts-loader": "^9.3.1",
"ts-node": "^8.3.0",
"tslint": "^6.1.0",
"typescript": "~4.6.4",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
}
}
app-routing.module.ts
const routes: Routes = [
{
path: '', pathMatch: "full", redirectTo: '/home'
},
{
path: 'home', pathMatch: "full", component: HomeComponent
},
{
path: 'first', pathMatch: "full", component: FirstComponent
},
{ path: 'category/:paramOne', component: SecondComponent },
{ path: '**', component: NotFoundComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload', initialNavigation: 'enabled' })],
exports: [RouterModule]
})
export class AppRoutingModule { }
server.ts
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/myApp/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => {
// res.status(404).send('data requests are not supported');
// });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
console.log("server.ts=> * =>req.url: ", req.url);
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
app.component.ts
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'myApp';
constructor(private router: Router) {
let currentRoute = "";
this.router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
console.log('Route change detected');
}
if (event instanceof NavigationEnd) {
currentRoute = event.url;
console.log(event);
}
if (event instanceof NavigationError) {
console.log(event.error);
}
});
}
}
logs
Node Express server listening on http://localhost:4000
NavigationEnd {
id: 1,
url: '/first',
urlAfterRedirects: '/first'
}
NavigationEnd {
id: 1,
url: '/img/menu_249e9f3331e6f8b23256.svg',
urlAfterRedirects: '/img/menu_249e9f3331e6f8b23256.svg'
}
NavigationEnd {
id: 1,
url: '/fonts/Apercu-Regular.woff',
urlAfterRedirects: '/fonts/Apercu-Regular.woff'
}
NavigationEnd {
id: 1,
url: '/fonts/Apercu-Light.woff',
urlAfterRedirects: '/fonts/Apercu-Light.woff'
}
NavigationEnd {
id: 1,
url: '/fonts/Apercu-Bold.woff',
urlAfterRedirects: '/fonts/Apercu-Bold.woff'
}
No navigation logs happens after this.
Note: There's also serverStateInterceptor and browserStateInterceptor services for caching requests. However I am sure that they're not the cause and so have avoided adding them here for readability. caching transfer state implementation used from here.