After quite some digging I've decided to post the question and hopefully a proper answer will appear :)
Goal: to have an angular application deployed in k8s, and exposing it via the nginx ingress with a customizable path AND parametrized routes. Call that should be working:
http://my-url/my-app/
http://my-url/my-app/a
http://my-url/my-app/b
http://my-url/my-app/c
http://my-url/my-app/c/my-id
Problem: the parametrized route /c/:id
is not supported. The factory is not called, and therefore the dynamic APP_BASE_HREF
is not set. Call that is not working fine:
http://my-url/my-app/c/my-id
There, the APP_BASE_URL
is not properly detected, and angular tries to load resources from http://my-url/my-app/c/runtime.js
.
There will be hard and long to provide a full code example, but some snippets will be provided for
Kubernetes
nginx
helm uninstall -n ingress-nginx nginx-ingress; helm install --namespace ingress-nginx nginx-ingress stable/nginx-ingress
(chart version: nginx-ingress-1.33.5; app version: 0.30.0)
ingress configuration
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "my-app.ui.fullname" . }}
labels:
app: {{ template "my-app.name" . }}
component: "{{ .Values.ui.name }}"
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/rewrite-target: /$2
ingress.kubernetes.io/ssl-redirect: "false"
ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: "{{ .Values.root_url }}"
http:
paths:
- path: "{{ .Values.ui.ingress.path }}(/|$)(.*)"
backend:
serviceName: {{ template "my-app.ui.fullname" . }}
servicePort: 8085
where {{ .Values.ui.ingress.path }}
could be anything like /my-app
angular application
src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
src/app/routing.module.ts
The routes we have.
const routes: Routes = [
{ path: '',
redirectTo: '/overview',
data: { title: 'Overview'},
pathMatch: 'full' },
{ path: 'overview',
data: { title: 'Overview'},
component: AComponent },
{ path: 'b',
data: { title: 'B'},
component: BComponent },
{ path: 'c/:id',
data: { title: 'C detail'},
component: CComponent },
{ path: 'c',
data: { title: 'C detail'},
component: CComponent },
{ path: '**',
component: PageNotFoundComponent }
];
@NgModule({
imports: [ RouterModule.forRoot(routes,
{enableTracing: false}
) ],
exports: [ RouterModule ]
})
export class RoutingModule { }
export const ROUTES_PATH = routes.map(p => p["path"])
src/app/app.module.ts
Just taking the APP_BASE_HREF
modifier:
import { getBaseLocation } from './common-utils';
/**
* Modules
*/
@NgModule({
declarations: [
AppComponent,
...
],
imports: [
BrowserModule,
RoutingModule
...
],
providers: [
Title,
{
provide: APP_BASE_HREF,
useFactory: getBaseLocation
}
],
bootstrap: [ AppComponent ],
entryComponents: [ t ]
})
export class AppModule { }
src/app/common-utils.ts
import { ROUTES_PATH } from './routing.module';
export function getBaseLocation() {
let paths: string[] = location.pathname.split('/');
let basePath: string = (paths && !ROUTES_PATH.includes(paths[1]) && paths[1]) || ''; // Default: ''
return '/' + basePath;
}
References
Providerfactory for APP_BASE_HREF token is called before APP_INITIALIZER is done
https://github.com/angular/angular/issues/25932
Angular 2 Set APP_BASE_HREF with a value from a Promise / Observable