mithril.js icon indicating copy to clipboard operation
mithril.js copied to clipboard

Nested routes

Open porsager opened this issue 7 years ago • 5 comments

Description

Allowing nested routes as object to allow splitting route subsections in different files etc.

Why

I copy the snippet below between all my projects using m.route, and I've seen a lot of other people ask for it. (eg. on gitter)

Possible Implementation & Open Questions

Support supplying a nested routes object directly to m.route().

flems example

This is what I use, but using it / something like it to normalize routes in core should be fairly trivial..

function flattenRoutes(routes, prefix = '') {
  return Object.keys(routes).reduce((acc, match) =>
    routes[match].view || routes[match].onmatch || routes[match].render
      ? { ...acc, [prefix + match]: routes[match] }
      : { ...acc, ...flattenRoutes(routes[match], prefix + match) }
    , {})
}

Is this something you're interested in working on?

Yes

porsager avatar Dec 02 '18 14:12 porsager

Prior art: Moria

barneycarroll avatar Dec 02 '18 18:12 barneycarroll

@porsager If you added it to v2, you'd need to be careful to only consider properties starting with a / as a route. Also, this raises a few questions:

  1. Should route objects and components intersect?
  2. Should route objects and route resolvers intersect?
  3. If the answer to 2 is "yes", what would be the expected behavior for render?
  4. If the answer to 2 is "yes", what would be the expected behavior for onmatch?

The first three are probably pretty obvious: the first two should be allowed, and render should not be called on child match. The last one is much less obvious, because there's multiple useful ways you could go about it:

  1. onmatch is called for all resolvers in the path and all awaited sequentially. The child route object could be optionally replaced or set on resolution, and if no child route matches, it falls back to using the parent itself as the end resolver.
  2. onmatch is called for all resolvers in the path and all awaited in parallel, but only the child-most route would be rendered.
  3. onmatch is called only for the child-most route to match.

The first would lend itself well to dynamic async route loading, but could lead to counter-intuitive behavior. The second would lend itself well to static async route loading, but would be simpler and slightly more consistent. The last is probably the simplest to implement and the simplest to understand: "/child": child..._.mapKeys(child, c => "/child" + c).

dead-claudia avatar Dec 02 '18 22:12 dead-claudia

I think you're misunderstanding my proposal.. I don't want any nested logic. Could you try to look again? I simply want to be able to give an object that has objects as values for keys, and if those objects aren't components or route resolvers the keys in the route through the tree are simply concatenated into the final route to match. (check the output from flattenRoutes in the flems).

porsager avatar Dec 02 '18 22:12 porsager

@porsager

I think you're misunderstanding my proposal.. I don't want any nested logic. Could you try to look again? I simply want to be able to give an object that has objects as values for keys, and if those objects aren't components or route resolvers the keys in the route through the tree are simply concatenated into the final route to match. (check the output from flattenRoutes in the flems).

Okay, so that reads like option 3 to me, onmatch only being called in the child-most route. Apologies for reading over the issue a little too quickly.

dead-claudia avatar Dec 02 '18 22:12 dead-claudia

Yeah, I'm not contemplating you should be able to mix paths with components or route resolvers.

Merely a way to split out routes to allow structuring projects a little better. I don't even think it would be a breaking change.

porsager avatar Dec 02 '18 22:12 porsager