Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Public router service #819

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

SergeAstapov
Copy link
Contributor

@SergeAstapov SergeAstapov commented May 12, 2022

@rwjblue rwjblue self-assigned this May 12, 2022
@rwjblue rwjblue added T-framework RFCs that impact the ember.js library T-ember-engines RFCs that impact the ember-engines library labels May 12, 2022
@chriskrycho
Copy link
Contributor

Thanks for writing this up! One question I have: is the idea that the router subclass could override all parent methods? Or should some of them be private (in TS terms and also in actual TS implementation)?

@SergeAstapov
Copy link
Contributor Author

@chriskrycho as currently only methods listed as part of public API are actually implemented in RouterService, I assume we should keep all the methods as public and overridable as ember-engines would need to do those overrides.

@runspired
Copy link
Contributor

runspired commented May 12, 2022

is the idea that the router subclass could override all parent methods? Or should some of them be private (in TS terms and also in actual TS implementation)?

Confirm what @SergeAstapov said, currently there is no private API in the router service, and Ideally it stays that way (basically as more of an interface).

This has the practical effect that at least (for now) overridden methods should eventually call super in order to have actual routing things take effect, minimizing the potential for folks to get too ---creative--- with what they might do as the signature and intent will have to remain aligned.

I considered whether we should call this out in the RFC and opted against, mostly because it seemed to fall under "public APIs are public" and telling someone they must call super and must extend the base-class would artificially (and only pedantically) limit experimentation in this layer that is desired.

Copy link

@villander villander left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really loved this RFC, thanks for doing that @SergeAstapov, but I think we need to keep the readability and isolation principle on top of our head to keep moving on in a direction to provide compatibility without losing the ergonomy 😄

* links to external routes in templates within the engine rendered via `<LinkTo />` component:

```hbs
<LinkTo @route="external-route">Go Somewhere</LinkTo>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still thinking that we should use <LinkTo @route="external.home">Go Somewhere</LinkTo> where external (hypothetical name) means a prefix that it's an external route. It's good for ergonomics because as a developer we don't want to go to externalRoutes definition and check if it's a internal or external route. I see the importance of deprecating <LinkToExternal/> but I don't want to lose the readability that it means an external link without poking around the code.

Also it solve the drawback on this RFC 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@villander this would prevent addons from utilizing LinkTo, which I think makes it a non-starter. It might be interesting to bring the external-routes map into routes.js at some point to address this though so that there's only one location where valid routes may be defined. This would be outside the scope of this RFC.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking for this perspective I agree with you @runspired I think we just need to find some way to don't make engines more confusing and harder to teach than it already is.

This is not viable long term solution as any changes made to `<LinkTo />` component in the Ember.js codebase
would need to be backported to `ember-engines`.

* Deprecate and remove `externalRoutes` mapping.
Copy link

@villander villander May 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 It breaks the isolation principle around ember-engines, where every engine could access any outside contexts without asking for it, going in the opposite direction of code isolation proposed by Engines.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, that's why though this is an alternative it's not the suggested path of the RFC

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely, just wanted to highlight and provide a reminder/support to the next reviews 😃


1. Replace use of `<LinkToExternal @route="external-route" />` component
with `<LinkTo @route="external-route" />` in every engine codebase.
2. Users using `ember-engines-router-service` would need to replace any of its usage with

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
2. Users using `ember-engines-router-service` would need to replace any of its usage with
2. Users using [ember-engines-router-service](https://github.com/villander/ember-engines-router-service) would need to replace any of its usage with

export default class extends Component {
@service router;
@action clickLink () {
this.router.transitionTo('external-route');
Copy link

@villander villander May 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I'd like to have something like that to don't lose the explicit outside context on engines. @rwjblue @dgeb and I made huge progress on that in the past - ember-engines/ember-engines#779

import { external } from 'ember-engines/helper'
...
@action 
clickLink () {
   this.router.transitionTo(external('external-route'));
 }

Copy link
Contributor

@runspired runspired May 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels unnecessary to impose an extra function call on users to mark something as external when it has already been defined as external in their router configuration (see also comments above on external.). Similarly, this would prevent addons from making use of the router service cleanly.

Copy link

@villander villander May 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends, I kinda agree with you that mapping those things on the router configuration clarifies things on a certain level, but I love readable and explicit code. How would you realize just looking for a ton of transitionTo in your code that those routes are from the host app? imagine you have at least 50 occurrences how many are linking outside context? Correct me if I'm wrong, IMO is necessary back in forth on the router configuration to realize those things.

@chriskrycho
Copy link
Contributor

Re: "no private API"—I want to be clear here about two things:

  1. I don’t have a vested interest in the particular decision we make here! I just want to make sure we make it carefully and with clear understanding of what we're committing to. 😄

  2. Directly related: the fact that everything has been public so far has been uninteresting just insofar as it has not been subclass-able and therefore not overridable. Supporting subclasses overriding methods has very different semantic and maintenance implications than public call-ability. (This comes up in any kind of formal analysis of types, whether dynamic or static; ask me how I know. 😂) In particular, public call-ability does not have the same implications for upholding internal-only invariants that subclass-ability does.

Net, I think that it's worth being very explicit and intentional about this question and I would actually like to see it addressed explicitly in the RFC. I'd also like to see some actual analysis of the existing public API: do we actually want to say that it's fine for subclasses to override the definition of currentURL, for example? Or of location, or the timing and behavior of firing routeWillChange and routeDidChange events? Maybe, but that's not obvious to me!

Accordingly, I think it would be helpful to delineate, for each of these (perhaps at the category level if appropriate, but at a per-item level if that is more appropriate in other cases) whether it should be overridable by subclasses or not—

Methods:

Properties:

Events:


Bonus doh comment: it can’t actually use private in TS for the implementation details, because it is public for call-ability. But per both general good norms and per the SemVer for TS Types spec, it’s perfectly fine for us to define what the public API supports in terms of subclassing!

@chancancode
Copy link
Member

(FYI we didn't get to this agenda topic this week because #811 and #812 took up most of the time, will try again next week)

@chancancode
Copy link
Member

chancancode commented May 13, 2022

I don't have a ton of context on this, but maybe this comment from @ef4 will be important/useful to y'all since you have been thinking about it?

This RFC seeks to provide a solution that also solves another DX issue faced by engines developers.

Authors of large scale applications often create an addon(s) to contain reusable components that can
be rendered either in the context of the host App or Engine. In such scenarios an extra guard needs
Copy link
Sponsor Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is also solved by using hrefs instead of route/model/query args of the default link-to.
with an a tag with preventDefaultd click handler, we can use the router service to detect when an href is in the route, or in an external app, and appropriately "do the right thing.

I like this approach because it means we:

here is a demo: https://codesandbox.io/s/custom-link-component-dgbxl?file=/app/components/link.hbs
(this demo is incomplete, but gives the gist).
(except codesandbox doesn't work so... 🤷 (this sandbox has been untouched since 3.21, and the dependency checker is preventing start up... idk what's going on. ))

where improvements could be made here where <LinkTo> is a bit better at:

  • query-param only transitions
  • sticky query params

Copy link
Sponsor Contributor

@NullVoxPopuli NullVoxPopuli May 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though, unifying on LinkTo is still a good improvement!
(over multiple components)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This RFC is what would make those router helpers and the href approach work correctly for engines as well.

@wagenet
Copy link
Member

wagenet commented Jul 25, 2022

It looks like this has some interest so we should work to keep it moving.

@villander
Copy link

Can we move this forward?

@runspired @chriskrycho @wagenet @chancancode

@achambers
Copy link
Contributor

Can we move this to Exploring?

@wagenet wagenet added S-Exploring In the Exploring RFC Stage S-Proposed In the Proposed Stage and removed S-Proposed In the Proposed Stage S-Exploring In the Exploring RFC Stage labels Sep 22, 2023
@wagenet
Copy link
Member

wagenet commented Sep 22, 2023

We had some back and forth in the Review meeting today around whether this should move to Exploring or not. There's definitely agreement that this is a problem we need to resolve, but that it's probably not going to be something that we can resolve until the new router design is more finalized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-Proposed In the Proposed Stage T-ember-engines RFCs that impact the ember-engines library T-framework RFCs that impact the ember.js library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants