1

Problem: I am configuring routes for my application. I want to have my url like https://localhost:4200/hero=id where id will be what ever user selects from Heroscomponent.This is not working for me.

if I try below url whose path is /hero/:id as per angular docs it works phonemically.

https://localhost:4200/hero/:id

Can some please provide me a solution to this problem.

This is my route config file where

 const appRoutes: Routes = [
  { path: 'hero', component: HeroesComponent },
  {path: 'hero{=:id}', component: HeroDetailComponent},
  {
    path: 'home',
    redirectTo: '/hero',
    data: { title: 'Heroes List' }
  },{
    path: 'student',
    component: AppTable
  },{
    path: 'video',
    component: VideoTagComponent
  },{ path: '',
    redirectTo: '/hero',
    pathMatch: 'full'
  }
  // { path: '**', component: PageNotFoundComponent }
];

Below is my HeroesComponent file where i am routing to path = "/hero="+id

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import {Hero} from './hero';


const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { id: 14, name: 'Celeritas' },
  { id: 15, name: 'Magneta' },
  { id: 16, name: 'RubberMan' },
  { id: 17, name: 'Dynama' },
  { id: 18, name: 'Dr IQ' },
  { id: 19, name: 'Magma' },
  { id: 20, name: 'Tornado' }
];

@Component({
    selector: 'my-heroes',
    templateUrl: './heroes.html',
    styleUrls: ['./app.component.css']
})

export class HeroesComponent {
    hero = HEROES;
    path:string;
    selectedHero: Hero;

    constructor(private router: Router){}        

    onSelect(hero: Hero): void {
      this.selectedHero = hero;
      this.path = "/hero=" +this.selectedHero.id.toString();
      this.router.navigate([this.path]);
    }

    // gotoDetail(): void {
    // }
}

This is the error i am getting in the browser console.
core.es5.js:1020 ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'hero%3D13' Error: Cannot match any routes. URL Segment: 'hero%3D13'**strong text**

Derek Brown
  • 4,232
  • 4
  • 27
  • 44
harsh
  • 247
  • 1
  • 2
  • 12

2 Answers2

1

I found a solution to this problem.

Because Angular don't support this.. So, we can create a CustomUrlSerializer

   import { UrlSerializer, UrlTree, DefaultUrlSerializer } from '@angular/router';

export class CustomUrlSerializer implements UrlSerializer {
    public parse(url: any): UrlTree {
        let _serializer = new DefaultUrlSerializer();
        return _serializer.parse(url.replace('=', '%3D'));
    }

    public serialize(tree: UrlTree): any {     
        let _serializer = new DefaultUrlSerializer();
        let path = _serializer.serialize(tree);
        // use regex to replace as per the requirement.
        return path.replace(/%3D/g, '=');
    }
}

import this module where you are bootstrapping your AppComponent

import { CustomUrlSerializer } from 'file path';

@NgModule({
  bootstrap: [ AppComponent ],
  imports: [
  ],
  providers: [
    { provide: UrlSerializer, useClass: CustomUrlSerializer},

  ]
})

In Your routing module create a matcher for mapping the route.

export const ROUTES: Routes = [
  { matcher: pathMatcher, component: ComponetName},
  ];

const KEYWORD = /hero=([^?]*)/;


export function pathMatcher(url: UrlSegment[], group: UrlSegmentGroup, route: Route): any {
    if (url.length >= 1) {
        const [part1] = url
        if (part1.path.startsWith('hero')) {
            const [,keyword] = KEYWORD.exec(part1.path) || ['',''];
            let consumedUrl: UrlSegment[] = [];
            consumedUrl.push(url[0]);
            return {
                consumed: consumedUrl,
                posParams: { //The parameters if any you want to pass to ur component
                    heroId: new UrlSegment(keyword,{})
                }
            }
        }
    }
    return null
}

Now, In your component you can fetch the heroId by using

this.route.params.subscribe((params)=>{
          this.data = params['heroId'];
      })

where route is instance of ActivatedRoute

harsh
  • 247
  • 1
  • 2
  • 12
0

Instead of {path: 'hero{=:id}', component: HeroDetailComponent},
use {path: 'hero/:id', component: HeroDetailComponent},
or {path: 'hero/:id/detail', component: HeroDetailComponent},.

According to angular documentation: you should create the path in that way: { path: 'hero/:id', component: HeroDetailComponent },

And in your HeroDetailComponent you'll be able to access the id in this way:

constructor(
  private route: ActivatedRoute,
  private router: Router,
  private service: HeroService
) {}

ngOnInit() {
  const hero = this.route.paramMap
    .switchMap((params: ParamMap) =>
      this.service.getHero(params.get('id')));
} 

more details official Documentation

mihai
  • 2,746
  • 3
  • 35
  • 56
  • @meorfi Thanks for your help. I understand you are referring to angular docs. However, I need to configure a route like {path: 'hero{=:id}', component: HeroDetailComponent}, – harsh Sep 20 '17 at 05:21
  • @harsh, not sure if routing allows special characters in paths. You should use queryParams to be able to set `id=1`, `key=a&value=b` ot whatever else, but you'll break best practices. See diff between query vs path params here :https://stackoverflow.com/a/31261026/896258 – mihai Sep 20 '17 at 06:35
  • @meorfi I understand that everyone should follows the best practices. Might be we are doing it wrong the problem is we are migrating from angular 1.x to 4. So, can't change the current SEO url's of our application. I tried hardcoding the path like "/hero=13" it works. However if i am creating url dynamically it's not working somehow. I also tried overriding the router UrlSerializer of router acc. to this https://stackoverflow.com/questions/39541185/custom-encoding-for-urls-using-angular-2-router-using-a-sign-in-place-of-a-sp but this is not overriding the DefaultUrlSerializer. – harsh Sep 20 '17 at 10:20