Router in global navigation always give me / even using relative path

77 Views Asked by At

I'm trying to create an app with a global nav (in AppComponent before router-outlet) and subroutes. The web app is built on the multi-tenant model with an url like this http://serveraddress/tenantSpace/:tenantUrl/ and I can't get the result I want in my routerLinks relative paths. I always get a / instead of full param path.

Here is what I tried:

So the route of each tenant will start after /tenantSpace/:tenantUrl/. So what I have for my routerLinks in my navigation is something like:

<a routerLink="./page1">Page1</a>

which I expect will give me:

<a routerLink="/tenantSpace/tenantSpace1/page1" href="/tenantSpace/tenantSpace1/page1">Page1</a>

The problem is that I always get /page1. The relative path is not evaluated even if I'm on a page at the same level as Page1 for example in /tenantSpace/tenantSpace1/page2.

If I put a routerlink inside the page1 component I get the good router.url but when the global navigation in AppComponent is evaluated the router.url is always /

So I'm sure I don't understand the way routing works in Angular and I don't find any ressources on this. If someone could help I would be very grateful.

Here is a stackblitz to illustrate the issue (you will find my route configuration inside):

Sample project

2

There are 2 best solutions below

1
Naren Murali On BEST ANSWER

Acutally ./ routing means the relative position of the current route of the component, since its in app.component the relative positioning is / so angular is working fine!

To solve your problem, you need to create a component called webspaces this will contain <router-outlet/> inside that we will subscribe the latest value of the params webSpaceName and store it in a service. Like so

webspaces.com.ts

import { Component } from '@angular/core';
import { ActivatedRoute, RouterOutlet, Params } from '@angular/router';
import { WebspaceService } from '../webspace.service';
import { Subscription } from 'rxjs';
@Component({
  selector: 'app-webspaces',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './webspaces.component.html',
  styleUrl: './webspaces.component.scss',
})
export class WebspacesComponent {
  subscription: Subscription = new Subscription();
  constructor(
    private activatedRoute: ActivatedRoute,
    private webspaceService: WebspaceService
  ) {
    this.subscription.add(
      this.activatedRoute.params.subscribe((params: Params) => {
        this.webspaceService.webspace = params?.['webSpaceName'] || '';
      })
    );
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

webspaces html

<router-outlet/>

In the root component when we route, we append the parameter and then perform the routing as shown below!

app html

<nav>
  <a [routerLink]="['webspaces', webspace, 'properties']">Properties</a>
</nav>
<router-outlet></router-outlet>

app ts

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink, RouterModule, RouterOutlet } from '@angular/router';
import { WebspaceService } from './webspace.service';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet, RouterModule, RouterLink],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  title = 'AppTest';

  get webspace() {
    return this.webspaceService.webspace;
  }

  constructor(private webspaceService: WebspaceService) {}
}

stackblitz -> cd test -> npm i -> npm run start

0
Senna Sanzo On

Thanks to @Naren Murali I ended with something like this:

In AppComponent template :

<a [routerLink]="[routerLinkBaseUrl(), 'page1']" routerLinkActive>Page1</a>

In my AppComponent.ts :

private _router = inject(Router);
public routerLinkBaseUrl = toSignal(this._router.events.pipe(
    filter((e):e is NavigationEnd => e instanceof NavigationEnd),
    map(e => {
      const matches = e.urlAfterRedirects.match(/\/tenants\/[\w-]*\d[\w-]*\/?/g);
      return matches ? matches[0] : "./";
    })
  ), { initialValue: ""});

It is not exaclty what you proprosed but I finally used the router events to get the navigationEnd and create a signal based on it.

If someone see a better way to do this...