yewstack/yew_router

URL Fragment Routing

pythoneer opened this issue · 6 comments

Description
In the recent pull request #133 with an addition to the readme, it is suggested to configure the server in a specific way. I think this is at least a little bit problematic and there needs to be a solution coming entirely form the frontend that is less intrusive to the server.

Other frameworks solve this problem be "encoding" the route as an URL Fragment

To reiterate on the problem:
if one has a route to www.example.com/user/12/detail the browser would request a document from the server at the location www.example.com/user/12/detail/index.html that is not present thus resulting in a 404. The current suggestion is layed out in #133 .

The proposed solution would be to use URL Fragments (also known as named anchors) to prevent the browser to request a new document from the server altogether. The route translated to the example above would look like this www.example.com/#/user/12/detail

prior art: https://angular.io/api/common/HashLocationStrategy

Downside: it's old and probably obsolete where html5 pushState exists. Its still "problematic" for entry point / shared links.

Fragment based routing is possible to a very limited degree at the moment, although the parser is partially limited in the allowed syntax (eg, currently it doesn't allow ? or / to appear after a #).

Defining all of your routes like #[to="#route"] is possible, but due to the restrictions on / not appearing after #, you are only limited to a single path segment, which severely limits the viability of this approach.

I personally don't like fragment based routing as it strikes me as outdated and ugly as mentioned, and people should be encouraged to use the full route instead, but I don't see a reason to prevent it if it could otherwise be done - given how it saves effort of setting up the server in a certain way.

A reasonable alteration to address this suggestion would be to make the parser less strict in what it is able to parse in the fragment section, so that normal routes can be expressed there.

Ok, taking a look at all the links you sent. I like the idea of HashLocationStrategy. Configuring the RouteService (and by extension, the RouteAgent) with a default type parameter that controls if a # is prepended to the route when setting and matching the browser's location seems to be the best way to accomplish this.

Something like:

pub struct RouteService<STATE, R: RouteBehavior = NoOp> {...}

It allows for the parser to remain unchanged, allows for a reasonable default, and for customization of routing behavior at compile-time.


EDIT:
This change is breaking, and it will arrive in a 0.7.0 release

This sounds like a very reasonable way towards a solution.

Thinking about this issue as I'm about to implement it, I don't think that a default type parameter is as good of a solution that I thought.

Because Yew uses specific type information to look up agents, every type that depends on the RouteAgent needs to have a bridge or dispatcher connecting to the exact same RouteAgent type. This allows for possible misuse when someone wants to use a plain RouteButton to set the route for a RouteAgent<Frag> instantiated by a Router<Frag>. The plain button will spawn a RouteAgent<PlainRoute> and set the route, bypassing the RouteAgent<Frag> used by the router. In addition to setting the route incorrectly, the Router also wouldn't receive a message telling it to update, because it isn't connected to the agent issuing the updates.

As a consequence of this, some stateful flag needs to be passed to the RouterService, and a special message must be created on the agent in order to enable this setting.

I have settled on just making the parser more permissive, accepting pretty much arbitrary tokens after a # is encountered (except these characters "!{}", which still operate as expected). I think adding type parameters would be error prone, and adding state to the agent/service to represent that a # should be inserted/ignored before the rest of the route would be inelegant and similarly error prone.

It is unfortunate that you will now have to sprinkle # at the beginnings of every route if you want to use fragment routing, but this is the most expedient and arguably best way to get fragment routing working.

Thanks for putting so much though into it! I believe that the use of this feature will continue to decline over time and a less intrusive solution sounds like the way to go.