'Different routes same component

I want to achieve something like this /products shows all the products and /products/:category shows all the products related to a specific category. To achieve that I have the following routes:

const productsRoutes: Routes = [
  {
    path: 'products',
    component: ProductsComponent,
    children: [
      {
        path: '',
        component: ProductsListComponent,
      },
      {
        path: ':category',
        component: ProductsListComponent
      }
    ]
  }
];

Problem

When I switch between categories, everything is fine, when I switch between all products and category products, and vice-versa, Angular redraws the components and there's a flickering.

Angular 2 Router final version doesn't have Regex,as for as I know. Is there something that I'm missing, or for now this is the only solution?



Solution 1:[1]

you can solve it by adding routes

const routes: Routes = [
    { path: 'experience',
        children: [
            { path: 'pending', component: ExperienceComponent },
            { path: 'requests', component: ExperienceComponent },
        ] }]

and in ExperienceComponent import

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

and in constructor add this parameter

constructor(public route: ActivatedRoute)

and inside constructor get url

this.route.url.subscribe(params => {
  console.log(params[0].path);
})

Solution 2:[2]

I don't know if there is another way to do this but I managed to make it work using the following hack.

export const productsRoutes: Route[] = [
    {
        path: 'products',
        component: ProductsComponent,
        children: [
            {
                path: '',
                pathMatch: 'prefix',
                component: ProductsListComponent,
                children: [
                    {
                        path: '',
                        component: EmptyComponent,
                    },
                    {
                        path: ':category',
                        component: EmptyComponent,
                    },
                ],
            },
        ],
    },
];

EmptyComponent:

import { Component } from '@angular/core';

@Component({
    selector: 'GemainEmpty',
    template: '<router-outlet></router-outlet>',
})
export class EmptyComponent {
}

Handle route changes on the ListComponent:

ngOnInit() {
    this.routerEventsSubscription = this.router.events.subscribe((evt) => {
        if (!(evt instanceof NavigationEnd)) {
            return;
        }
        //Code to reload/filter list
    }
}

And add a router outlet on the ListComponent template.

Solution 3:[3]

You could also define a redirect to a specific path:

{ path: '**', redirectTo: '/home', pathMatch: 'full' },

where /home is the route you want to redirect to.

path: '**' resolves all paths which are not defined

Solution 4:[4]

You can solve it by using redirect,

const productsRoutes: Routes = [
  {
    path: 'products',
    component: ProductsComponent,
    children: [
        {
            // path => '/products'
            path: '',
            redirectTo: ':category',
        },
        {
            // path => '/products/:category'
            path: ':category',
            component: ProductsListComponent
        }
    ]
  }
];

It's more like set one path default, unless there is a matching path.

Solution 5:[5]

For problems like 'angular router optional parameter', 'angular route component reuse':

UrlMatcher is for creating a custom route matcher for a route pattern (which leads to your desired component). As a result, a customized route pattern is going to be handled, and the corresponding requests are handled by that component.

In the routes array, { matcher: productsMatcher, component: ProductsListComponent} is a route, just as { path: 'somewhere', component: SomeComponent } that defines a route.

A matcher takes the whole url string as an array of segments. The segments can be assigned to route params, which can be read by the component.

import { RouterModule, Routes, UrlMatchResult, UrlSegment } from '@angular/router';


const productsMatcher = (segments: UrlSegment[]): UrlMatchResult => {
  if (segments.length === 1 && segments[0].path === 'products') {
    return {
      consumed: segments,
      posParams: {}
    }
  }
  if (segments.length === 2 && segments[0].path === 'products') {
    return {
      consumed: segments,
      posParams: {
        category: segments[1]
      }
    }
  }
  return <UrlMatchResult>(null as any);
}

const routes: Routes = [
  // { path: '/products', component: ProductsListComponent },
  // { path: '/products/:category', component: ProductsListComponent },
  { matcher: productsMatcher, component: ProductsListComponent},
];

angular doc

In your component

  ngOnInit(): void {
    this.route.paramMap.subscribe(param => {
      const category = param.get('category')
      if (category) { this.loadStuff(category) }
    })
  }

All requests of the same url pattern are directed to this component.

As a result, (after it is created at the first request) there are no redraws/recreation of the ProductsListComponent. Thus there's no flickering when visiting /products/:category route from /products route: angular sees it as the same route, so the component won't reload.

For reference 1 2

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mohamed Abo Elmagd
Solution 2 Guilherme Meireles
Solution 3 Flosut Mözil
Solution 4
Solution 5