rfcs
rfcs copied to clipboard
Support navigating to hash URLs for in-page navigation
if supported, maybe hash / anchor / id navigation would look like this in user-land:
export default class Router extends EmberRouter {
location = config.locationType;
rootURL = config.rootURL;
constructor() {
super(...arguments);
this.on('routeDidChange', this.handleHashTarget);
}
willDestroy() {
this.off('routeDidChange', this.handleHashTarget);
}
@action
handleHashTarget() {
schedule('afterRender', () => later(this, 'scrollToId', 16));
}
@action
scrollToId() {
let id = this.location.hash;
if (id) {
document.getElementById(id)?.scrollIntoView();
}
}
}
(and then letting the actual router (router_js) handle # navigations. atm, the navigation look like:
Attempting URL transition to /my-route#hashId
Attempting URL transition to /my-route
It just drops the hash
How would this play together with location type hash? I guess it could be only supported for history location?
I like this proposal!
Perhaps because I've proposed something similar earlier: 😄
What if the Ember Router introduced a new type of "sub-leaf" (could be called e.g. variation), that could be encoded as
my/path#variation-{name}(for example)?With routable variations we could, for example, show modals (i.e. routable modals) or toggle tabs on a page. Such route variations would share the modal hook (that hook wouldn't re-run when switching between variations) and the variation state would be accessible from templates and controllers (if Ember decides to keep them around).
Link to my similar idea: https://github.com/emberjs/rfcs/issues/662
We should think about what other use-cases there are. For example encoding open modals, scroll position, etc. As mentioned in my quoted issue above, there is some precedent in CSS :target, in using this for more than just scroll position.
@jelhan For location type hash we could split the hash on some character, e.g. /index.html#/my/route~hashId or some of the other valid chars as separators.
https://stackoverflow.com/questions/2849756/list-of-valid-characters-for-the-fragment-identifier-in-an-url/2849800
Made a thing: https://github.com/CrowdStrike/ember-url-hash-polyfill
location type hash would not / can't be supported, afaik
I think a first iteration on that feature should add support for URL fragments in general. This would enable different use cases. Scrolling to an anchor after transition would be one of them. Persisting state, which should not be sent to the server in the URL, would be another.
To support URLs fragments, I think the following changes are needed:
RouterService.transitionTo()andRouterService.replaceWith()methods accept ahashas part of the existing options object. I think the following rules should apply:- If
hashis present, the URL fragment of the target URL should be set to the provided value. - If
hashis not present andtransitionToorreplaceWithis invoked with route and/or models arguments, an existing URL fragment is removed.transitionToorreplaceWithis invoked with options has only, an existing URL fragment stays as it.
- If
RouterService.urlFor()accepts ahashas part of existing options object. If present it is added as URL fragment to the generated URL.RouterService.currentURLreturns the current URL including the URL fragment. (I think this already works.)<LinkTo>accept a@hashargument.- The URL fragment of the URL used as
hrefattribute is,- the value of
@hashargument if set, - not present if
@hashargument is not set and either@route,@modeland/or@modelsargument is set, - the URL fragment of the current URL if not set and neither
@route, nor@modelnor@modelsarguments are set.
- the value of
@hashargument is treated the same as forRouterService.transitionTo()andRouterService.replaceWith()on the transition triggered on click.
- The URL fragment of the URL used as
Open questions:
- Should a
hashproperty be added toRouteInfo? The URL fragment is not associated with any specific route. But it would be helpful to have access to it inTransition.toandTransition.from. I guess it's the same question as ifurlproperty should be added toRouteInfoor not. - We we add support for
hashoption toRoute.transitionTo(),Route.replaceWith(),Controller.transitionToRoute()andController.replaceRoute()even though they are deprecated? What to do withReplace.intermediateTransitionTo(), which is not (yet) deprecated? - Should we support transition type
hash? Or should we limit support to transition typehistoryat least for the first iteration?
Sounds good! I think we should swap @hash for @anchor tho, per the nomenclature here: https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL
This also avoids the naming overload we'd have with the (hash) helper
Sounds good! I think we should swap
@hashfor@anchortho, per the nomenclature here: https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URLThis also avoids the naming overload we'd have with the
(hash)helper
Naming is very difficult here in my opinion. I used hash to follow the naming of URL interface. But it doesn't seem to be a clear case:
- In RFC 3986: Uniform Resource Identifier (URI): Generic Syntax it is called fragment.
- URL standard maintained by WHATWG uses both terms fragment and hash in 2.6 URLs. It calls the component of the URI
fragment, while the attribute representing it in the Interfaces for URL manipulation is named hash. - I also heard the term anchor you proposed often. But I wasn't able to find it in any spec with that meaning. In HTML spec I found the term anchor used in
HTMLAnchorElementinterface, which represents a<a>tag. But I didn't found it referencing a specific part of the URL.
I fully agree that it is confusing if {{hash}} helper would be used together with a @hash argument:
<LinkTo @query={{hash sortBy="title"}} @hash="person-13">
It doesn't get easier as existing argument for setting query parameters does not follow naming in URL interface and relevant specification. It is named @query for <LinkTo> and queryParams for the options object accepted by RouterService.transitionTo() etc. This is inline with RFC 3986, which reference them as query. But on URL interface has search and searchParams properties. URL standard maintained by WHATWG uses both terms (again): query for the URI component and search as attribute name on the interface for URL manipulation.
I would tend to follow the URL interface so that it maps well to native JavaScript API. But that is already not true for existing query and queryParams. :sob:
I used hash to follow the naming of URL interface.
oh no. even the URL docs are contradictory! 🙃
yeah, following URL interface is probs best
@jelhan For location type hash we could split the hash on some character, e.g.
/index.html#/my/route~hashIdor some of the other valid chars as separators.
There doesn't seem to be any character, which is allowed in a fragment but is not allowed in a path or query component of a URI. :cry:
Accordingly to RFC 3986: Uniform Resource Identifier (URI): Generic Syntax a fragment may contain the following characters:
fragment = *( pchar / "/" / "?" )
That's exactly the same as for query component of an URI:
query = *( pchar / "/" / "?" )
And a subset of which is allowed in path component of an URI:
path = path-abempty ; begins with "/" or is empty
/ path-absolute ; begins with "/" but not "//"
/ path-noscheme ; begins with a non-colon segment
/ path-rootless ; begins with a segment
/ path-empty ; zero characters
path-abempty = *( "/" segment )
path-absolute = "/" [ segment-nz *( "/" segment ) ]
path-noscheme = segment-nz-nc *( "/" segment )
path-rootless = segment-nz *( "/" segment )
path-empty = 0<pchar>
segment = *pchar
segment-nz = 1*pchar
segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
; non-zero-length segment without any colon ":"
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
I think it would be fine to split on an uncommon character, such as ~. I know there is a theoretical collision risk, but maybe we could just raise an exception instead of setting a hash where it would collide. Also, could make the separator character configurable (with a default), so anyone who happens to use ~ (or whatever we go for; $ and * could also work) could swap it out.
This would be a great feature. Is there an actual path to RFC here?
potentially. :thinking: the least hacky thing we'd need for this is to have some for he framework to let us know that rendering after a transition has "settled" (even keeping in mind that pages could spin up intervals and all that, which keep us from normal settled state)
The re-working of the routing layer may be a prereq for this tho, as the existing routing layer is opt-in to everything, which is kind of annoying to work with (QPs, no hashes at all, etc). We'd have to add in hash support to RouteInfo, etc, where keeping that information should just be default.
I wonder if the new Polaris router ideas would resolve this.
I saw this comment in another thread:
I got feedback from a framework team member early this year that it is unlikely to land any RFC changing existing routing behavior currently. It sounded as framework team wants to have a clear vision for a Polaris router before touching existing stuff. Mainly to avoid unnecessary churn. I paused all activities on this topic due to that feedback.
— Jelhan (https://github.com/emberjs/rfcs/issues/787#issuecomment-1195685756)
@sandstrom I was just on a core team meeting where the router stuff was being discussed. We want to be clear that active progress is being made here and very soon we should have some more public information and be able to help rope the community in a bit more!
@wagenet did things ever get to a place where you could share more publicly?