Igor Zinchenko
Igor Zinchenko

Reputation: 353

Angular routing correct way to using different component for same path

I have an issue with Angular routing I have two modules Basic and Dashboard

In the basic module, I can pass city param for not default city (for default city it should work without param, for example, if New York is default city the path will be http://my.domain/new-york and APP should use HomeComponent) but for not default application should use path http://my.domain/los-angeles and use the same component. List of cities I've got from the back-end. Next level of my structure is categories. I've got categories from the back-end too, but if I try to go to the http://my.domain/category I've got HomeComponent instead of SearchComponent. What the correct way to handle that?

I need:

http://my.domain/category -> SearchComponent
http://my.domain/city/category -> SearchComponent
http://my.domain/city -> HomeComponent
http://my.domain/ -> HomeComponent

app route config:

const routes: Routes = [
  {
    path: 'dashboard',
    canActivate: [AuthService],
    loadChildren: () => import('./modules/dashboard/dashboard.module').then(mod => mod.DashboardModule)
  },
  {
    path: '',
    loadChildren: () => import('./modules/basic/basic.module').then(mod => mod.BasicModule),
    runGuardsAndResolvers: 'always'
  },
  {
    path: '**',
    redirectTo: '/'
  }
];

Basic module routing

const routes: Routes = [
  {
    path: '',
    component: BasicComponent,
    children: [
      {
        path: '',
        component: HomeComponent,
        data: {
          headerDark: true
        }
      },
      {
        path: 'search',
        component: SearchComponent,
        pathMatch: 'full'
      },
      {
        path: ':city',
        component: HomeComponent,
        data: {
          headerDark: true
        },
      },
      {
        path: ':category',
        component: SearchComponent
      },
      {
        path: ':city/:category',
        component: SearchComponent
      },
      {
        path: ':city/search',
        component: SearchComponent,
        data: {
          headerDark: true
        }
      }
    ]
  }
];

Upvotes: 1

Views: 1413

Answers (2)

Andrei Gătej
Andrei Gătej

Reputation: 11924

I think you can solve it by implementing a custom UrlMatcher.

Here's the type definition for UrlMatcher:

export type UrlMatchResult = {
  consumed: UrlSegment[];
  posParams?: {[name: string]: UrlSegment};
};

export type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) =>
    UrlMatchResult|null;

Angular uses a defaultUrlMatcher for each of the route configuration objects. Its role is to determine the positional params(e.g :city) and the consumed segments(the segments that match the issued URL):

/* ... */
for (let index = 0; index < parts.length; index++) {
  const part = parts[index];
  const segment = segments[index];
  const isParameter = part.startsWith(':');
  if (isParameter) {
    posParams[part.substring(1)] = segment;
  } else if (part !== segment.path) {
    // The actual URL part does not match the config, no match
    return null;
  }
}

return {consumed: segments.slice(0, parts.length), posParams};

With this in mind, we could create a custom UrlMatcher that looks like this:

const cityMatcher: UrlMatcher = (segments, group, route) => {
  // you'll have to find the issued city name from `segments`
  // simply use console.log(segments) to find the proper way to do it
  // after you've found the name of the city, check if it is in the list of cities

  // if NOT in the list
  return null

  // if it is there
  return {
    consumed: segments.slice(0, 1),
    posParams: { city: nameOfTheFoundCity }
  }
}

And can be used like this:

{
  {
    path: ':city',
    component: HomeComponent,
    data: {
      headerDark: true
    },
    matcher: cityMatcher
  },
}

The same approach can be used for :category.

Upvotes: 3

R. de Ruijter
R. de Ruijter

Reputation: 573

I don't have the answer for you, but it probably has something to do with the path matching strategy.

Look here to see if adding pathMatch: 'Full' to one of your routes can solve your problem! I don't know which exact route you should add the pathMatch full strategy to but I believe the answer to your problem lies here.

Cheers

Upvotes: 0

Related Questions