Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Router with regular expression #12442

Closed
felipedrumond opened this issue Oct 21, 2016 · 12 comments
Closed

Router with regular expression #12442

felipedrumond opened this issue Oct 21, 2016 · 12 comments
Labels
area: router feature Issue that requests a new feature freq2: medium

Comments

@felipedrumond
Copy link

I'm submitting a ... (check one with "x")

[ ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[x] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

I understand that is highly recommended to not use this tool when looking for support, but after spending hours of search and attempts to solve this issue, I don't have any other option but ask the community here.
In angular 2 beta.9-12 there was a way to use a regular expression to determine if some component should be used:

{   regex: '[a-z]+.[0-9]+',
    serializer: (params) => `{params.a}.params.b}`,
    component: MyComponent }  

This option is not available anymore ("@angular/router": "3.1.0"), so I'm struggling to redirect some urls do the right component. For instance, I can't distinguish between the two routes:

{ path: ':categoryUrl/:productUrl' } -> goes do product.component
{ path: ':categoryUrl/:page' } -> goes to category.component

Expected behavior

Router should provide a way to let us specify regular expressions to define if some route should be activated or not.

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

Keep organization of urls, specially keep existing SEO ranking.

Please tell us about your environment:

  • Angular version: 2.0.X

  • Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]

  • Language: [all | TypeScript X.X | ES6/7 | ES5]

  • Node (for AoT issues): node --version =

@vsavkin vsavkin added feature Issue that requests a new feature freq2: medium labels Oct 31, 2016
vsavkin added a commit to vsavkin/angular that referenced this issue Nov 8, 2016
vsavkin added a commit to vsavkin/angular that referenced this issue Nov 9, 2016
@vicb vicb closed this as completed in 7340735 Nov 10, 2016
@rafaelss95
Copy link
Contributor

Is there any place that I can see how to use this new matcher? I read the part in API reference and the link for UrlMatcher seems broken.

@matanshukry
Copy link

I read the code a bit, and found a way to use a custom url matcher for regex. Enjoy:

https://gist.github.com/matanshukry/22fae5dba9c307baf0f364a9c9f7c115

(it includes both declaration of a custom url matcher and usage)

@vicb
Copy link
Contributor

vicb commented Feb 8, 2017

@matanshukry thanks for sharing

@thefill
Copy link

thefill commented May 7, 2017

Any chance to add gist by @matanshukry to the official documentation? As far as I can see a link to documentation for urlmatcher on the official documentation for router is still broken: broken url.

@ronaksharma8
Copy link

ronaksharma8 commented May 22, 2017

@matanshukry :- i have replicated your code into my demo project.... but it shows error "Expression form not supported" while building project , currently i am using lastest version of angular i,e angular4... can you please guide me if i am misssing something.

My requirement is as follows :-

../xyz/abc/file/asd/dsa/das
../xyz/abc/file/qwe/dfg/yrf

if url contains "file" word, it should redirect to specific component.

@ftabaro
Copy link

ftabaro commented Sep 4, 2017

@matanshukry GIST do not pass AoT compilation. When using JiT it first fail, if triggering the watch functionality, compilation works:

ERROR in Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function (position 5:10 in the original .ts file), resolving symbol ComplexUrlMatcher in /home/ftabaro/Projects/mobidb3/mobidb3-ui/src/app/components/entry-page/url-matcher.ts, resolving symbol entryRoutes in /home/ftabaro/Projects/mobidb3/mobidb3-ui/src/app/components/entry-page/entry-page.routing.module.ts, resolving symbol EntryRoutingModule in /home/ftabaro/Projects/mobidb3/mobidb3-ui/src/app/components/entry-page/entry-page.routing.module.ts, resolving symbol EntryRoutingModule in /home/ftabaro/Projects/mobidb3/mobidb3-ui/src/app/components/entry-page/entry-page.routing.module.ts, resolving symbol EntryRoutingModule in /home/ftabaro/Projects/mobidb3/mobidb3-ui/src/app/components/entry-page/entry-page.routing.module.ts

The ComplexUrlMatcher function (taken from the above mentioned GIST):

import {Route, UrlMatchResult, UrlSegment, UrlSegmentGroup} from '@angular/router';


export function ComplexUrlMatcher(paramName: string, regex: RegExp) {
  return function (segments: Array<UrlSegment>,
                   segmentGroup: UrlSegmentGroup,
                   route: Route): UrlMatchResult {

    const parts = [regex];
    const posParams: { [key: string]: UrlSegment } = {};
    const consumed: UrlSegment[] = [];

    let currentIndex = 0;

    for (let i = 0; i < parts.length; ++i) {
      if (currentIndex >= segments.length) {
        return null;
      }

      const current = segments[currentIndex];
      const part = parts[i];

      if (!part.test(current.path)) {
        return null;
      }

      posParams[paramName] = current;
      consumed.push(current);
      currentIndex++;
    }

    if (route.pathMatch === 'full' &&
      (segmentGroup.hasChildren() || currentIndex < segments.length)) {
      return null;
    }

    return {consumed, posParams};
  }
}

And my routing module:

export const entryRoutes: Routes = [
  {
    matcher: ComplexUrlMatcher('unp', new RegExp('[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2}')),
    component: TheComponent,
    loadChildren: 'd',
    resolve: {
      entry: ResolveData
    },
    children: [
      {
        path: 'a',
        component: ComponentA
      }, {
        path: 'b',
        component: ComponentB
      }, {
        path: 'c',
        component: ComponentC
      }, {
        path: 'd',
        component: ComponentD
      }
    ]
  },
];

@NgModule({
  imports: [
    RouterModule.forChild(entryRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class EntryRoutingModule {
}

Finally my package.json:

{
  "name": "myApp",
  "version": "0.1.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "4.3.3",
    "@angular/cdk": "2.0.0-beta.10",
    "@angular/common": "4.3.3",
    "@angular/compiler": "4.3.3",
    "@angular/compiler-cli": "4.3.3",
    "@angular/core": "4.3.3",
    "@angular/forms": "4.3.3",
    "@angular/http": "4.3.3",
    "@angular/language-service": "4.3.3",
    "@angular/material": "2.0.0-beta.10",
    "@angular/platform-browser": "4.3.3",
    "@angular/platform-browser-dynamic": "4.3.3",
    "@angular/router": "4.3.3",
    "@types/node": "6.0.85",
    "core-js": "2.5.0",
    "d3": "4.10.0",
    "hammerjs": "^2.0.8",
    "jasmine-core": "2.6.4",
    "jasmine-spec-reporter": "4.1.1",
    "karma": "1.7.1",
    "ngl": "^0.10.3",
    "rxjs": "5.4.2",
    "zone.js": "0.8.14"
  },
  "devDependencies": {
    "@angular/cli": "1.1.1",
    "@angular/compiler-cli": "^4.0.0",
    "@angular/language-service": "^4.0.0",
    "@types/jasmine": "2.5.45",
    "@types/node": "~6.0.60",
    "codelyzer": "~3.0.1",
    "jasmine-core": "~2.6.2",
    "jasmine-spec-reporter": "~4.1.0",
    "karma": "~1.7.0",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "protractor": "~5.1.2",
    "ts-node": "~3.0.4",
    "tslint": "~5.3.2",
    "typescript": "~2.3.3"
  }
}

Should this be reopened?

@tytskyi
Copy link

tytskyi commented Sep 4, 2017

@darwinFailure AOT compiler cannot analize an inplace call (as it says in error message), try

export function myMatcher() {
  return ComplexUrlMatcher('unp', new RegExp('[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2}'));
}

// then in config replace
{matcher: myMatcher}

@ftabaro
Copy link

ftabaro commented Sep 4, 2017

I realized it some seconds after posting! thanks for the support.

Got it working by providing to the matcher an handle to a function whose signature matched Angular's requested one:

export function ComplexUrlMatcher(segments: Array<UrlSegment>,
                           segmentGroup: UrlSegmentGroup,
                           route: Route): UrlMatchResult {

  const paramName = 'unp';
  const regex = new RegExp('[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2}');

  ....

  }

// then in config
{ matcher: ComplexUrlMatcher, ... }

@tytskyi your solution is much more elegant than mine but it triggers some error about the function signature :/

ERROR in /home/ftabaro/Projects/mobidb3/mobidb3-ui/src/app/components/entry-page/entry-page.routing.module.ts (13,14): Type '{ matcher: () => (segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route) => UrlMatc...' is not assignable to type 'Route[]'.
  Type '{ matcher: () => (segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route) => UrlMatc...' is not assignable to type 'Route'.
    Types of property 'matcher' are incompatible.
      Type '() => (segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route) => UrlMatchResult' is not assignable to type 'UrlMatcher'.
        Type '(segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route) => UrlMatchResult' is not assignable to type 'UrlMatchResult'.
          Property 'consumed' is missing in type '(segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route) => UrlMatchResult'.

@jrsanchezalcala
Copy link

jrsanchezalcala commented Dec 21, 2017

I find a easy way to make url regex. The solution is to create a component that reads parms and redirects to this params based on regex . I do an example.

First you need to create a component similar like these

`
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";

@Component({
  selector: 'app-redrouter',
  templateUrl: './redrouter.component.html',
  styleUrls: ['./redrouter.component.scss']
})
export class RedrouterComponent implements OnInit {
  routing = [];

  constructor(private route: ActivatedRoute, private router: Router) {

    this.routing = [
      {patter: "", redir: ""}


    ]


  }

  ngOnInit() {

 
      this.enroutar(this.router.url);
    

  }

  enroutar(val: string) {
    for (let r of this.routing) {
      if (this.match(r.patter, val)) {
        this.router.navigate([r.redir])
      }
    }
  }

  match(patter, value) {

    var patt = new RegExp(patter);
    return patt.test(value);
  }


}

`

and then you have to put at end of your router the next code

{ path: '**' , component: RedrouterComponent },

I hope could help.

@Reinbert
Copy link

The problem with jrsanchezalcala's suggestion is that you are creating another entry in the history stack if you router.navigate() away.

@sushrest
Copy link

@Reinbert Agree.. this should be handled at router level.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: router feature Issue that requests a new feature freq2: medium
Projects
None yet
Development

No branches or pull requests