Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 Routing Path-Style Parameters

Using the @angular/router version 3.0.0-beta.2 I would like to build an application which allows my users to browse a filesystem.

The following illustrates the types of URL I would like to support:

http://myapp/browse                   <--- Browse without parameters, lists root dir
http://myapp/browse/animals           <--- Browse "animals" subdirectory
http://myapp/browse/animals/mammals   <--- Browse "animals/mammals" subdirectory

I am struggling to come up with a mechanism to configure this using RouterConfig.


Path-style Parameters

{ path: 'browse/:path', component: BrowserComponent }

This RouterConfig only supports a single subdirectory, i.e. browse/animals. However, if I attempt to browse to the second-level of subdirectory /browse/animals/mammals I get the following error:

Error: Cannot match any routes: '/browse/animals/mammals'

I understand that the default behaviour of a parameter in the router (in this case :path) is to "break" on the first forward slash, and assume the following token is a sub-path.

How can I configure the router to allow the :path parameter to accept forward slashes / "gobble" the entire remaining URL as the parameter?


Optional Parameter

How can I deal with the use-case where the user wishes to browse the root directory, i.e. the URL /browse. This will not match the router configuration described above, as the :path parameter is not present.

I have attempted to work around this using two distinctly configured router paths:

{ path: 'browse', component: BrowserComponent },
{ path: 'browse/:path', component: BrowserComponent }

However, this is causing me problems with routerLinkActive. I have a main menu with a link to the browser, that looks like this:

<a [routerLink]="['/browse']" [routerLinkActive]="['active']">...</a>

I would like this main menu link to have the active class if the user has browsed to the root /browse or to some subdirectory /browse/animals/.

However, given the /browse/animals/ URL is not a child of the /browse route, it does not become active.

I cannot make the :path parameter route a child of the /browse root, as there is no need for a nested view, and it results in:

Cannot find primary outlet to load BrowserComponent
like image 888
jwa Avatar asked Jul 21 '16 10:07

jwa


People also ask

What is path in Angular routing?

The path refers to the part of the URL that determines a unique view that should be displayed, and component refers to the Angular component that needs to be associated with a path. Based on a route definition that we provide (via a static RouterModule.

What is a route parameter?

Route parameters are named URL segments that are used to capture the values specified at their position in the URL. The captured values are populated in the req. params object, with the name of the route parameter specified in the path as their respective keys.


Video Answer


1 Answers

Building on the ** suggestion from @DaSch, it is indeed possible to use the wildcard route:

{ path: '/browse', component: BrowserComponent, children: [ { path: '**' } ] }

There is no need to have a nested router-outlet within the BrowserComponent as there is no component field specified on the child path.

As desired, the BrowserComponent will now be routed for all the described URLs:

http://myapp/browse
http://myapp/browse/animals
http://myapp/browse/animals/mammals

The challenge now is to obtain the par tof the URL which represents the path that the browser has navigated to, and subscribe to changes as the user navigates.

I have prototyped this using the Router:

router.events.filter(e => e instanceof NavigationEnd)
  .forEach((event: NavigationEnd) => {

    // The root route belongs to "/browse"
    let parentRoot: ActivatedRoute = router.routerState.root.firstChild;
    if (parentRoot.snapshot.url.map(p => p.path).join("/") == "/browse") {

      let path: string = "";

      // Child route is optional, in case the user has browsed to just "/browse"
      let childRoute: ActivatedRoute = parentRoot.firstChild;
      if (childRoute) {
        path = childRoute.snapshot.url.map(p => p.path).join("/");
        console.log("New browser path is ", path);
      }

      this.loadPath(path);
    }
  }
);
like image 88
jwa Avatar answered Oct 21 '22 03:10

jwa