5

This question is a duplicate of the following existing questions here on SO:

Angular router: how to replace param?

Generically replace Angular 2 route parameter and navigate

Angular 6 router - replace some of the parameters of the current route

Angular2-router: How to only change a parameter of a route?

How can I change a specific, internal route parameter in Angular2

None of the answers given to these questions fully solves the problem of replacing one segment in the url, therefore I am asking this question again.

Summarizing my situation:

These are some of the routes in my project (I did not come up with this myself, so please bear with me)

/:customer/static/part/of/url
/:customer/other/static/stuff
/something/for/the/:customer
/something/without/anything
/other/stuff/:customer/might/need

The Parameter ":customer" appears in various places of the url. My problem is that this customer-id might change in the backend without any action from the frontend. I am listening to changes on the backend and have to adjust the url accordingly in case the customer-id changes.

A simple string.replace won't do because technically the customer-id might actually and literally be "static" or "other".

Question:

How can I analyze the current url (ActivatedRoute?) to replace only the one part of the url concerning the customer-id (paramMap has "customer") while keeping the rest of the url as it is?

Tobias Gassmann
  • 11,399
  • 15
  • 58
  • 92

2 Answers2

3

I did write own generator. Here is the code:

import { ActivatedRoute } from "@angular/router";

const defaultExtras: { 
  params: any, 
  preserveMatrix: boolean, 
  recursive: boolean, 
  preserveChildMatrix: boolean, 
  replaceChildParams: boolean 
} = {
  params: {},
  preserveMatrix: true,
  recursive: true,
  preserveChildMatrix: false,
  replaceChildParams: true
};

export function routerPathGenerator(
  route: ActivatedRoute, 
  extras?: { params?: any, preserveMatrix?: boolean, recursive?: boolean, preserveChildMatrix?: boolean, replaceChildParams?: boolean }
): any[] {
  const mergedExtras = { ...defaultExtras, ...extras };
  const path: any[] = [...replaceRouteParams(route.routeConfig.path.split('/'), { ...route.snapshot.params, ...mergedExtras.params })];

  if (mergedExtras.preserveMatrix) {
    path.push(replaceMatrixParams(route, mergedExtras.params));
  }

  if (mergedExtras.recursive && route.children.length > 0) {
    path.push(...routerPathGenerator(route.children[0], {
      params: mergedExtras.replaceChildParams ? mergedExtras.params : {},
      preserveMatrix: mergedExtras.preserveChildMatrix,
      recursive: mergedExtras.recursive,
      preserveChildMatrix: mergedExtras.preserveChildMatrix,
      replaceChildParams: mergedExtras.replaceChildParams
    }));
  }

  return path;
}


function replaceRouteParams(parts: string[], params: Object): any[] {
  return (parts || [])
    .map(part => (part.startsWith(':') && params.hasOwnProperty(part.substring(1)) ? params[part.substring(1)] : part));
}

function replaceMatrixParams(route: ActivatedRoute, params: Object): any {
  const matrix: any = {};
  Object.keys(route.snapshot.params)
    .forEach(key => 
      (route.routeConfig.path.split('/').some(p => p === ':' + key) ?
      '' : 
      matrix[key] = params.hasOwnProperty(key) ? params[key] : route.snapshot.params[key])
    )
  return matrix;
}

Suppose we have URL like this:

http://some.domain/warehouses/5;c=7;d=8/moves/42/items;a=1;b=2?e=77&f=88

where '/warehouses' points to lazy loaded module with routes:

const routes: Routes = [
  { path: '', component: WarehousesComponent },
  { path: ':id', redirectTo: ':id/stocks', pathMatch: 'full' },
  { path: ':id', component: WarehousesComponent, children: [
    { path: 'stocks', component: StocksComponent },
    { path: 'moves', component: MovesComponent, children: [
      { path: '', redirectTo: 'items', pathMatch: 'full' },
      { path: ':moveId/items', component: ItemsComponent },
      { path: 'initial', component: InitialComponent }
    ] }
  ] }
];

If I run the next code inside WarehousesComponent

    const commands = routerPathGenerator(this.route, {
      params: {
        id: 8,
        moveId: 24,
        c: 17,
        a: 666
      },
      preserveChildMatrix: true
    });

    this.router.navigate(commands, { queryParamsHandling: 'preserve', relativeTo: this.route.parent });

I'll navigate to

http://some.domain/warehouses/8;c=17;d=8/moves/24/items;a=666;b=2?e=77&f=88

I think it's your case just check extras params of routerPathGenerator

Dmytro Bardai
  • 154
  • 1
  • 7
1

To read a text pattern in a string, I would suggest a regular expression.

By example if your customer ID is like '123456' the regex can be /\d{6}/ (the slashes are here to describe the regex pattern, not the url ones) :

let url = '/something/for/the/123456' // 123456 denotes :customer here
let pattern = /\d{6}/;

let customerId = url.match(pattern);

Take care of defining a pattern that only matches the customer id in the URL.

You can find further informations on JavaScript regex here : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/RegExp

And I almost forgot, you can replace a pattern in a string using the String.replace() function : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/String/replace

Dali
  • 344
  • 1
  • 10
  • Thanks for you answer! I am trying not to use String.replace though, because theoretically the customer-id might be anything, including "something"... – Tobias Gassmann Jul 08 '19 at 13:20
  • If you don't know what your :customer will look like neither its position in the url I wish you good luck :) don't you have other informations ? – Dali Jul 08 '19 at 13:24
  • @Abel, there is a router config which instructs angular router service how to resolve paths to components.And the main question is how to use the 'metadata' from this config for url segment substitution. – Dmytro Bardai Aug 13 '19 at 07:44