Tomas Lukac
Tomas Lukac

Reputation: 2255

Angular routing for prefix with route parameter

I want to implement the user profile page. It would be simple to just use the

/profile/user123 path using:

app-routing.module

{
  path: 'profile',
  loadChildren: () => import('./modules/profile/profile.module').then(m => m.ProfileModule)
},

profile-routing.module

{
  path: ':username',
  component: ProfileComponent
},

however, I want to do some fancy URL like /@user123.

Unfortunately, I did not find any clues on how to do it. I tried the following:

app-routing.module

{
  path: '@:username',
  loadChildren: () => import('./modules/profile/profile.module').then(m => m.ProfileModule)
},

profile-routing.module

{
  path: '',
  component: ProfileComponent
},

but it didn't work.

There is only one thing that comes to my mind, which is using a Guard checking for the '@' prefix, otherwise redirect to the /not-found page.

Any ideas on how to do the routing using the "Angular" way?

Upvotes: 1

Views: 1732

Answers (3)

Tomas Lukac
Tomas Lukac

Reputation: 2255

I am adding another answer, since I also needed to use the @user123/edit path, but it didn't work with the original solution. So here is how to do it:

app-routing.module.ts

 {
    matcher: (url) => {
      console.log(url);
      if (url[0].path.match(/^@[\w.\-]+$/gm)) { <-- removed check for url length
        return {
          consumed: url.slice(0, 1), <-- return the rest of the URL instead of the whole URL (this routing rule should only consume @user123 and return the rest of the URL, which is /edit)
          posParams: {
            username: new UrlSegment(url[0].path.substr(1), {}) // <--- creating UrlSegment by getting rid of @ from url path
          }
        };
      }
      return null;
    },
    loadChildren: () => import('./modules/profile/profile.module').then(m => m.ProfileModule)
  },

profile.routing.module.ts

{
    path: '',
    component: ProfileComponent
  },
  {
    path: 'edit',
    component: EditProfileComponent
  },

Upvotes: 0

Tomas Lukac
Tomas Lukac

Reputation: 2255

The solution provided by Siddhant is excellent, but I wanted /@user123 instead of /profile/@user123, so I tweaked it a little.

app-routing.module.ts

{
  matcher: (url) => {
    if (url.length === 1 && url[0].path.match(/^@[\w.\-]+$/gm)) { // <--- added a hyphen and a dot, since \w only does [a-zA-Z0-9_]
      return {
        consumed: url,
        posParams: {
          username: new UrlSegment(url[0].path.substr(1), {}) // <--- creating UrlSegment by getting rid of @ from url path
        }
      };
    }
    return null;
  },
  loadChildren: () => import('./modules/profile/profile.module').then(m => m.ProfileModule)
},

profile.routing.module.ts

{
  path: '',
  component: ProfileComponent
},

profile.component.ts

ngOnInit(): void {
  this.activatedRoute.paramMap.subscribe(paramMap => {
    this.userService.getUser(paramMap.get('username')).subscribe(res => {
      if (res) {
        this.user = res;
      } else {
        this.router.navigate([Utils.ARTICLE_NOT_FOUND_URL]);
      }
    });
  });
}

Upvotes: 1

Siddhant
Siddhant

Reputation: 3171

You can make use of custom route matcher to achieve the expected result.

  • app-routing.module.ts
    {
      path: 'profile',
      loadChildren: () => import('./modules/profile/profile.module').then(m => m.ProfileModule)
    },
  • In profile-routing.module.ts you can specify a route by providing custom URL-matching function as:
    {
      matcher: (url) => {
        // You can have regex as per your requirement
        if (url.length === 1 && url[0].path.match(/^@[\w]+$/gm)) {
          return {
            consumed: url,
            posParams: {
              username: new UrlSegment(url[0].path.substr(1), {}) // <--- creating UrlSegment by getting rid of @ from url path
            }
          };
        }
    
        return null;
      },
      component: ProfileComponent
    }
  • In profile.component.ts file you can read the username as:
    username$!: Observable<string | null>;
    constructor(private activatedRoute: ActivatedRoute) { }

    ngOnInit(): void {
      this.username$ = this.activatedRoute.paramMap
        .pipe(
          map((params: ParamMap) => params.get('username'))
        );
    }
  • In profile.component.html you can print the username as:
    <p>
        Profile: {{username$ | async}}
    </p>
  • Now simply navigate to url /profile/@user123 and you should be able to get user123 as username in ProfileComponent

Upvotes: 2

Related Questions