-
Notifications
You must be signed in to change notification settings - Fork 24.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(router): Update Router to be
providedIn: 'root'
(#46914)
This commit updates the Router itself to be `providedIn: 'root'` with a factory function rather than provided in the `RouterModule`. PR Close #46914
- Loading branch information
1 parent
a8e9247
commit 0cbbd6a
Showing
7 changed files
with
363 additions
and
342 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1838,6 +1838,9 @@ | |
{ | ||
"name": "setUpAttributes" | ||
}, | ||
{ | ||
"name": "setupRouter" | ||
}, | ||
{ | ||
"name": "shallowEqual" | ||
}, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AndrewKushnir
FYI, this change was not mentioned in the changelog for 14.1.1 and I have the feeling it breaks our component, After upgrading from 14.1.0 to 14.1.1, we suddenly get the following error:
This is the only commit I could find in 14.1.1 that could cause this. Our special situation could probably have something to do with it as well. We have created a framework that hosts different microfrontends. The framework itself doesn't use routing, but app developers are allowed to use it in their own microfrontend (using a NullLocationStrategy for obvious reasons). They then call
RouterModule.forRoot()
in their app, which is in a lazy loaded module of course.To my understanding, a call to RouterModule.forRoot() would provide this
UrlSerializer
, correct?By hoisting the
Router
to the root of the framework application (where there is noRouterModule.forRoot
) I get the above error I assume. When I now change it so that my framework callsforRoot
and all the lazy modules classforChild
I get rid of the error but the routes don't work anymore (since the child routes are not loaded through routing). In our use case, these micro frontends can be opened and closed continuously, and there can also be multiple instances of the same micro frontend in the same view.I guess that, since the way we did it (see below) is no longer supported in 14.1.1 I now have to create a mechanism that calls
resetConfig
when opening or closing a micro frontend instance, but that's quite a lot of work :)This is the relevant module code of one of our apps:
0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like that with this change, it is no longer possible to have different routers for each lazy loaded module. If this is intentional, then it would at least make sense to share this as a breaking change, and not deliver this in a patch-release.
0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
14.1.2 includes the fix for the
NullInjector
. This happened unintentionally due to other required commits not making it into the patch release.Providing multiple Routers was never intentionally supported nor documented behavior. The
forRoot
naming and documentation indicate the intent and expected use be that it's provided in the root injector for the application.It's never the intent for us to make breaking changes, especially in patch releases. It's difficult to anticipate undocumented and unintended use of the existing APIs. Note that avoiding a breaking change is why other providers were kept in the return value of
forRoot
even though they could otherwise be omitted since they areprovidedIn: 'root'
now.I think a quick solution for your applications would be to add
Router
to theproviders
list ofAppWithRouterModule
. I can't tell exactly how you're creating these applications, but if they are created with a new injector in the hierarchy, they can get a separateRouter
instance by providing it again.Additionally, we'll investigate adding(Edit: It looks likeRouter
back to theROUTER_PROVIDERS
list so multiple calls toforRoot
in separate injectors would still result in multipleRouter
instances.Router
is still listed in the providers). Again, this certainly isn't the intended use, but we're really not trying to make breaking changes here. This would be considered more of a bug introduced in the patch release rather than an intentional breaking change.Edit: Does the
14.1.2
release work for you? It looks likeRouter
is still in the providers list offorRoot
so you'd get another instance if you called it multiple times. c7fed38 would fix the error for the root router instance.0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side note, there is absolutely already an attempt to prevent what you've found a way to do (call
RouterModule.forRoot
multiple times):angular/packages/router/src/router_module.ts
Lines 120 to 123 in 6454974
0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the reply! Well, the guard only prevents calling forRoot twice in the same injector hierarchy of course, what we do is call it in two separate hierarchies.
I've had success in providing Router in the created apps, although I had to change to forChild in the apps, do a forRoot in the framework, copy most of the setupRouter function (it is not exported for obvious reasons) and also provide the outlet administration (the ChildrenOutletContexts) in each injector separately (to prevent the "can not activate the active outlet" error.
We knew that we were using undocumented behavior of course, and we intend to improve our code. We just weren't expecting this change in this patch release. Thanks for your answer and effort!
0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@woppa684 Not sure if you saw my edit about the 14.1.2 release. Does that release fix your issue?
Again, there was absolutely a bug in 14.1.1 because
Router
wasn't actually capable of working withprovidedIn: 'root'
on its own.0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hadn't seen it yet, will give it a try tomorrow 👍🏼
0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, this does not solve my problem because now I get the
RouterModule.forRoot() called twice
error. The logical explanation would be that when I callforRoot
from a micro frontend, it iperforms the check, but the check injects theRouter
, which will be provided by theprovidedIn: 'root'
of course. So, technically I did NOT callforRoot
twice in the same Injector tree, but I can understand why it thinks I did ;) So, if this is indeed the case then I suggest that the error message is adapted to include something like... or forRoot not called from the root of the application
.So, with the latest changes I think I really need to implement something clever. I cannot override the check since its
InjectionToken
is not exported and providing my ownRouterModule
factory is probably alsno not easy (or desirable) since then I need to use / copy unexported details of the router.It would probably be best if I create a mechanism that keeps the router in the root of the framework and resets the config when child nodes are added or removed. I can use the instance id of the children as path and then append their own routes probably. Of course this would mean a breaking change for my clients, but so be it. Or do you see a simpler solution?
EDIT: After some experimenting, I think it's not really feasible with just one router, since (apart from dynamically updating the config, which I have managed to do) I would also have to dynamically name the outlets (to be all different) and dynamically alter the routerLinks to go to the correct outlet. Especially these last two things look impossible.
EDIT2: As an easy fix for now, I've found that "'unproviding" the
Router
in my root framework module solves the current problems. This might not be a solution for the longer term but at least we can move along with the latest version of Angular for now.0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, indeed the change would have the effect of actually fixing the check to be as strict as intended.
Agreed, we should adjust the error message.
👍
0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@atscott Of course, this "easy fix" doesn't work anymore in Ng15. With the introduction of the extracted
NavigationTransitions
, unproviding theRouter
is no longer sufficient. I would also need to reprovideNavigationTransitions
but unfortunately this token is not available in the public API, so it is always provided in the root and I cannot change that.I thought I would just mention this here and not create a separate issue for this since I'm working with an unsupported usecase.
I have no clue how to support my use case now, any thoughts are welcome :)
0cbbd6a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@woppa684 Right, this is very much not supported. Your best path forward if you want to support this long term without constantly being broken by upstream changes would be to fork the Router code and maintain this use-case in-house.