20

I was wondering if there is a way to name my routes in Angular7 so I could just call [routerLink]="myRouteName" instead of [routerLink]="/my/route/path".

If so, how can I achieve this?

RafaelTSCS
  • 1,234
  • 2
  • 15
  • 36
  • 3
    Use constants values. – Rohit.007 Dec 04 '18 at 14:18
  • There's nothing directly available to assign a name to a route. However, you can workaround by creating a route that is same as the name you indend to use (/myRouteName) and that can redirect to the deep route (/my/route/path). – Aragorn Dec 04 '18 at 14:21
  • How do you decide the routeName? Want to hard code while creating route config? – Ashish Ranjan Dec 04 '18 at 14:28

6 Answers6

18

Considering you want to configure the routes names while you are creating the route configuration.

Lets leverage routes' data property to add names to routes. (A small extra data in every route should not affect any performance).

But first, let's create a class which conatains a static property for holding route names and their actual paths.

export class RouteNames {
  public static routeNamesObject = {}
}

Now in your routing component where you have defined the routes, let's have it like:

const routes: Routes = [
  {path: "hello/:id", component: HelloComponent, data: {routeName: "Hello1"}},
  {path: "hello", component: HelloComponent, data: { routeName: "Hello2" }}
]

Just after this variable initialization set the RouteNames class's static prop

routes.forEach((eachRoute) => {
  RouteNames.routeNamesObject[eachRoute.data.routeName] = eachRoute.path;    // now all route paths are added to this prop
})

Make a public variable in your component to access the static class

Like in app.component.ts: (You don't need injection)

public routeNames = RouteNames;

Then the app.component.html will be something like:

<button [routerLink]="routeNames.routeNamesObject['Hello2']">Click to Navigate to Hello</button>
Ashish Ranjan
  • 12,760
  • 5
  • 27
  • 51
  • 2
    I thought about this approach, but I just wanted to make sure there was no other option. I guess I'll have to stick with something like this. Your solution is good, but I wonder if it doesn't bring too much complexity for this issue. Wouldn't it be better to just setup a .ts file with a constant and use it inside the routing modules and components? – RafaelTSCS Dec 04 '18 at 14:53
  • Oh yeah.. having a seperate file with constants having path names is much cooler! I think that is better! Using the constants both while configuring the routes and while using them in your components – Ashish Ranjan Dec 04 '18 at 14:57
  • 1
    A clean alternative for having to type `"routeNames.routeNamesObject['...']"` would be to define a pipe (say `url`) that does this, e.g. `routerLink="{{ 'Hello2' | url }}"`. This way you avoid having to define the route names locally in every component that needs them. – Safron May 01 '20 at 17:49
  • Also, instead of adding the name to the data property, you could consider creating an `interface NamedRoute implements Route { name: string}` and `type NamedRoutes = NamedRoute[]` and use those interfaces instead. A route would then simply look like `{path: "hello", component: HelloComponent, name: "Hello2" }`. – Safron May 01 '20 at 18:01
  • Creating a `NamedRouterModule` that registers the names in the `forRoot`/`forChild` method, can also get rid of the overhead of registering the names yourself with `routes.forEach(...)`. Maybe I should create package... – Safron May 01 '20 at 18:05
7
<a [routerLink]="routerLinkVariable"></a>

So this variable (routerLinkVariable) could be defined inside your class and it should have a value like below

export class myComponent {

     public routerLinkVariable = "/my/route/path"; // the value of the variable is string!
}
Mile Mijatović
  • 2,948
  • 2
  • 22
  • 41
7

I managed to do this like this (bonus if someone can tell me how to remove the Array loop):

custom-route.interface.ts -- this is to add name to our route object


export interface ICustomRoute extends Route {

  name?: string;

}

In routing.module.ts (or wherever you have stored your routes):

const routes: ICustomRoute[] = [
  { path: 'some/random/page1', component: TestComponent, name: 'page1'  },
  { path: 'some/random/page2', component: Test2Component, name: 'page2'  },
  { path: 'page3', component: Test3Component, name: 'page3'  },
];

Now the name is being passed into your routing module so now you have to catch the name and handle it. You can do this in a number of ways, I ended up using a directive (I saw @Safron mentioned a pipe as well)

named-routerlink.directive.ts

@Directive({
  selector: 'a[namedRouterLink]'
})
export class NamedRouterLinkDirective extends RouterLinkWithHref{
  @Input('namedRouterLink')

  set namedRouterLink(val: any) {
    const selectedRoute = this.myRoute.config.filter( x => x['name'] == val)[0];
    this.routerLink = "/" + selectedRoute['path']
  };

  constructor(router: Router, route: ActivatedRoute, locationStrategy: LocationStrategy, private myRoute: Router){
    super(router, route, locationStrategy)
  }

}

And then obviously use like this:

 <a namedRouterLink="page1">Page 1</a>

Pipe Version:

@Pipe({name: 'getRoute' })
export class RoutePipe implements PipeTransform {

  constructor(private router: Router){}

  transform(routeName: string): string {
    const selectedRoute = this.router.config.filter( x => x['name'] == routeName)[0];
    return "/" + selectedRoute['path']
  }

}

Pipe Example:

 <a namedRouterLink="{{ 'page1' | getRoute }}">Page 1</a>
PreQL
  • 358
  • 2
  • 6
4

You can use constant file for that so that it can be reusable in other components too.

export const ROUTE_URL = {
    myRouteName: '/my/route/path',
};

As many as you have.

then use that constant on .TS and use it in the view.

View:

<a [routerLink]="routerUrls.myRouteName"></a>

.TS:

public routerUrls= ROUTE_URL;

Even on your route file

 { path: 'xyz', loadChildren:  ROUTE_URL.myRouteName},
Rohit.007
  • 3,414
  • 2
  • 21
  • 33
  • Same setup we are using in our application to load routes from the backend API. we can use constant directly in the `.TS` in a variable and then that variable can be used in the view. We are using constant file for usability of code, otherwise that url can also be in that variable directly. – Rohit.007 Dec 04 '18 at 14:29
  • 1
    I've seen this approach, but I didn't feel much comfortable using it. So I was looking for a possible "out of the box" way of doing it. I guess I'll have to stick around with this one. – RafaelTSCS Dec 04 '18 at 14:54
  • How would the constant for a path with an id like this look like? `/my/contract//details/foo` ? – Datz Jun 30 '21 at 06:28
3

In your class

public routeName = '/customRoute/myExample'

Then in your template

[routerLink]="routeName"

This should pull that value through, i think thats what you are asking for. This will only work if the customRoute is actually a valid route name you have made.

devDan
  • 5,969
  • 3
  • 21
  • 40
0

This is old but crazy how Angular does not yet support this out of the box, when application gets bigger and there are a lot of URLs with a lot of slashes in it, it will be very hard very to remember it.

I've managed to create a pipe that support routes naming and it supports route params and query params, this is the code:

import { Pipe, PipeTransform } from '@angular/core';
import { Router, Route } from '@angular/router';
import {isUndefined} from "util";

@Pipe({
  name: 'path'
})
export class RoutingPipe implements PipeTransform {
  private _path: string = null;
  constructor(
    private _router: Router
  ){}

  transform(_route_name: any, params?: {}): any {
    this._path = null;
    this.findPath(this._router.config, _route_name);
    if(this._path == null) throw new Error('No path found for name "'+_route_name+'"!');
    if(params){
      for (let k in params) {
        if (params.hasOwnProperty(k)) {
           this._path = this._path.replace(`:${k}`, params[k]);
        }
      }
    }
    return this._path;
  }

  /**
   * navigate to component by route name
   *
   * @param name route name
   * @param params route params if exist
   */
  navigate(name: string, params?: {}){
    this._router.navigate([this.transform(name, params)]);
  }

  /**
   * Find route's full path by name
   *
   * @param config routes
   * @param name route name
   * @param parent
   */
  findPath(config: Route[], name: String, parent?: string) {
    parent = parent || '';

    for (let i = 0; i < config.length; i++) {
      const route: Route = config[i];
      const path = parent + '/' + route.path;
      if (route.children) {
        // this._path = null;
        const currentPath = route.path ? parent + '/' + route.path : parent;
        this.findPath(route.children, name, currentPath);
      }else{
        if(!isUndefined(route.data) && (<any>route.data).name == name){
          this._path = path;
        }
      }
    }
  }

}

declare it in the routing.ts file:

{ path: 'users/:id/edit', component: UserComponent, data: { name: 'edit_user' }}

You can use it like this in your html file:

<a [routerLink]="'edit_user'|path:35">Some link</a>

also inside your component.ts as a service (make sure to inject it in the constructor)

editUser(){
    let userID = // look for user id
    this._router.navigate('edit_user', {id: userID});
}
SlimenTN
  • 3,383
  • 7
  • 31
  • 78