Skip to content

UrlMatcher as a service #17145

Closed
Closed

Description

@PleasantD

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

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

Current behavior
Only a function can be set as the urlMatcher of a route.

const routes = [{
    urlMatcher: (segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route) => {
        /* Do stuff */
    },
    component: MyComponent,
}]

Expected behavior
A DI-resolved service or function can be set as the urlMatcher allowing for further injection. I'd expect this to work in an identical way to how the resolver properties can either be services that implement the Resolve<T> interface OR can be an inline resolve() function.

const routes = [{
    urlMatcher: MyUrlMatcherService,
    component: InterruptComponent,
},
{
    path: ":id",
    component: ListComponent, /* Contains a <router-outlet> for routing children */ 
    children: [ /*children with sub-paths for deep linking */ ]
}];

@Injectable()
export class MyUrlMatcherService {
    constructor(myStateService:MyStateService){}

    matchUrl(segments: UrlSegment[], segmentGroup: UrlSegmentGroup, route: Route):UrlMatchResult {
        if (!this.myStateService.isInterrupt) return null;
        /* Do matching stuff */
    }
}

Minimal reproduction of the problem with instructions
N/A

What is the motivation / use case for changing the behavior?
My use case is that InterruptComponent should resolve if some system state (managed by a service) is currently true. If not, I want to skip this route an let other routes attempt matching. In my case I want to greedily consume the entire sub-path and display a specific component, but only in certain circumstances.

Please tell us about your environment:
N/A

  • Angular version: 4.X
  • Browser: N/A
  • Language: TypeScript 2.2+
  • Node (for AoT issues): N/A

Activity

  1. changed the title [-]UrlMatcher as a servcice[/-] [+]UrlMatcher as a service[/+] on May 31, 2017
  2. added
    featureLabel used to distinguish feature request from other issues
    on May 31, 2017
  3. Dok11 commented on Sep 7, 2017

    @Dok11
    Contributor

    Hi all! Have any news about this brilliant feature?

  4. added this to the Backlog milestone on Jan 23, 2018
  5. MatissJanis commented on Mar 28, 2018

    @MatissJanis

    Would be absolutely amazing if we could have this!

  6. skorenb commented on Jun 18, 2018

    @skorenb

    Also, it would be nice to have an ability to return Promise<UrlMatchResult > in matchUrl function

  7. Dok11 commented on Jun 18, 2018

    @Dok11
    Contributor

    @KorenB why Promise? It is one-time action?
    I think what matchUrl can runs several times, so it must be is Observble.

  8. skorenb commented on Jun 19, 2018

    @skorenb

    @Dok11 , you're right! it should support Observable. Because sometimes i need to call api before the matcher has resolved

  9. adrigardi90 commented on Jul 11, 2018

    @adrigardi90

    I agree! If the matchUrl function could support asynchronous behaviour, would be really good

  10. EddyP23 commented on Aug 30, 2018

    @EddyP23
    Contributor

    @KorenB, do you have a workaround for the case when you need to call API before the matcher has resolved?

  11. EddyP23 commented on Aug 30, 2018

    @EddyP23
    Contributor

    and +1 for the feature, would be very useful

  12. amandabot commented on Aug 30, 2018

    @amandabot

    @EddyP23
    In our application, we need information about a user from a remote source to determine which route to use. We load the user at login time and cache it in a service, as well as storing a global reference to the Angular injector. The matcher imports the injector, finds the user service, and returns the cached user through Observable.of(), which is synchronous. It's a bit hacky, but it works.

    In short, the route which needs an asynchronous matcher would need to be preceded by a route that doesn't, and that initial route would need to get the remote information via a resolver or a service injected into a component.

    If your workflow doesn't give you the opportunity to preload the data, you could have a route that presents a loading screen with a distinct URL, do the async request, and redirect to the route with the async matcher.

  13. joselcvarela commented on Aug 7, 2019

    @joselcvarela

    It's been a year since last comment and I would love to have this feature on my application.
    Specifically I would want to inject Ngrx Store into matcher so I can conditionally match the route accordingly to my store.

  14. SachinShekhar commented on Jan 1, 2020

    @SachinShekhar

    I also need this feature badly. It'd be much easier to inject other services into this than grabbing Injector instance from main.ts. Plus, asynchronous path matching is a necessity. Unfortunately, matcher doesn't take async functions.

    Example Use Case: A URL segment can represent a tweet, user, group and I want to lazily load a module only after finding out what this segment is post API call.

  15. 11 remaining items

  16. krokofant commented on Oct 18, 2021

    @krokofant

    @atscott I forgot to think about the combination of the existing UrlMatcher with a CanUse guard. It would be much cleaner with a one-two punch approach of first matching the URLs and then deciding if they're usable.

    I can't figure out any good example. 😅

  17. OysteinAmundsen commented on Nov 26, 2021

    @OysteinAmundsen

    I have an example where I need this:

    In my application I use a rather complex tree structured hierarchy of nodes of different types. The currently selected node is available from a service and each time a node is selected its entire path is rendered in the url as query params. The reason for this, is that the selected node structure is used accross several different routes. But the queryparams is not necessarily ordered as this can be invoked externally as well, so I depend on having the service available for determining which node is the currently selected one. For this one route in my app, which is for configuring nodes of specific types, each node type has its specific component.

    Right now, I do this is done inside a component wrapper where I run const node = service.getSelectedNode() and in the template several *ngIf="node.type === 'someType' to invoke the destination component.

    What I would like to do is:

    export const configRoutes: Routes = [
      {
        path: '',
        component: ConfigurationComponent,
        // FIXME: https://github.com/angular/angular/issues/17145
        children: [
          { matcher: createNodeMatcher('root'), component: RootFormComponent},
          { matcher: createNodeMatcher('assetGroup'), component: AssetGroupComponent},
          { matcher: createNodeMatcher('asset'), component: AssetFormComponent},
          { matcher: createNodeMatcher('section'), component: SectionFormComponent},
          { matcher: createNodeMatcher('level'), component: LevelFormComponent},
          { matcher: createNodeMatcher('zone'), component: ZoneFormComponent},
          { matcher: createNodeMatcher('room'), component: RoomFormComponent},
          { matcher: createNodeMatcher('resource'), component: ResourceFormComponent},
          { matcher: createNodeMatcher('assetMeteringPointGroup'), component: MeterGroupComponent },
        ],
      },
    ];
    
    export function createNodeMatcher(match: string): UrlMatcher {
      return (segments: UrlSegment[], group: UrlSegmentGroup, route: Route): UrlMatchResult | null => {
        // Need to inject the `service` somehow
        return service.getSelectedNode().type === match ? { consumed: segments } : null;
      };
    }
    
    export const ConfigurationRoutes = RouterModule.forChild(configRoutes);
    

    But the AssetTreeService needs to be injected somehow.

    Now that I think about it, can I configure the RouterModule injected to the module from the module constructor (where the injector is available)?

  18. added a commit that references this issue on Jun 10, 2022
    a99727f
  19. angular-automatic-lock-bot commented on Jul 14, 2022

    @angular-automatic-lock-bot

    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.

  20. locked and limited conversation to collaborators on Jul 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: routerfeatureLabel used to distinguish feature request from other issuesfeature: under considerationFeature request for which voting has completed and the request is now under consideration

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions