5
votes

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
5
Any reason you don't use version beta.2 of the router? I wouldn't expect this to work but several other issues were fixed.Günter Zöchbauer
@GünterZöchbauer No, there's no good reason I'm not using that version of the router. I'll bump forward my dependency.jwa

5 Answers

7
votes

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);
    }
  }
);
5
votes

I haven't tried this, but from the documentation, I would try to use children with a wildcard selector.

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

It's just guess and I would have to check how to retrieve the path parameters to get the right content for the given path.

0
votes

You could try it this way..

Just using your first route: { path: 'browse', component: BrowserComponent }

And for the folder names using so called optional routes.

The link should have this syntax here:

<a [routerLink]="['/browse', ['f1', 'f2', 'f3', 'f4'] ]">--link name--</a>

or like this:

<a [routerLink]="['/browse', { folders: ['f1', 'f2', 'f3', 'f4'] } ]">--link name--</a>

Inject private _route: ActivatedRoute to your Component's constructor and subscribe to the params:

this._route.params.subscribe(
      val => console.log(val),
      err => console.log(err));

The first will print this: Object {0: "f1", 1: "f2", 2: "f3", 3: "f4"} and the second will print this: Object {folders: "f1,f2,f3,f4"}

Or maybe you will get another idea to add your folders using this optional parameters.. gl! :)

-1
votes

You can configure the router dynamically like explained in https://stackoverflow.com/a/38096260/217408

router.resetConfig([
 { path: 'team/:id', component: TeamCmp, children: [
   { path: 'simple', component: SimpleCmp },
   { path: 'user/:name', component: UserCmp }
 ] }
]);

https://github.com/angular/angular/issues/11437#issuecomment-245995186 provides an RC.6 Plunker

-1
votes

Can useful, for me good worked router.resetConfig() with new route rules in custom logic of my app