You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Load a component in a route depending on an asynchronous condition #12088
Current behavior
Doesn't seem possible to do it in an easy way without not use framework core stuff
Expected behavior
To be able to determine by an asynchronous condition (a condition that will be resolved later) which component to load in a route.
What is the motivation / use case for changing the behavior?
I'd say the following is a very common behavior expected on any medium size app:
Let's say you have a dashboard (just after you login). You wanna keep 2 different components UserDashboard and AdminDashboard (for not filling your template code with *ngIf), but that condition cannot be resolved immediately because the role comes from an API, so then you cannot "right away" conditionally pass one component or another to a specific route.
Use CanActivate guard and redirect to an appropriate page based on user role.
Reacted by Michael Sanford and Jonathan Kolberg
Reacted by Raphi Stein, U.Ç. Doğan, john doe, Maihan Nijat, Yuri Pereira Constante, makdeniss, Guilherme Haschel, Matija Pevec, Ben Racicot, Markus Thiel and 2 more
But, doing that means you're redirecting to another route (let's say like "user-dashobard" or "admin-dashboard", while I wanted to keep them under "/dashboard".
Maybe it's just not the way it should be done? In that case, what are the drawbacks or the approach?
Maybe it's just not the way it should be done? In that case, what are the drawbacks or the approach?
IMO it would be super-confusing to have 2 completely different pages under the same URL. Imagine that someone who is an admin copies the /dashboard URL and sends it over to someone else that isn't. The other person would be able to access the page but would see completely different content from the intended one!
Reacted by Michael Sanford, Bogdan, Vivek Gawande, ozorOzora and Lazar Ljubenović
Reacted by Alexander Turtsevich, Karol Zawadzki, Simon Xiong, prog110, Conan , Alex Burykin, Richard Misiak, mattheuspirovani, Matiss Janis Aboltins, Digital Leader Author and 24 more
But, doing that means you're redirecting to another route (let's say like "user-dashobard" or "admin-dashboard", while I wanted to keep them under "/dashboard".
I know it's a limitation but there're a lot of real bugs in the router and I don't think requested feature will be implemented in the nearest future.
Reacted by Tomasz, Nguyen Thanh Dat B, Matiss Janis Aboltins, Digital Leader Author, Raphi Stein, Aidan Dunn, Patrick Geyer, Giulianno Sbrugnera, Alberto Rubio, Dmitry and 8 more
For a few different applications we've written in Angular 1.x, we have a route that takes in an itemId as a parameter. The item is then looked up by Id, and then the template/controller is selected based on the itemType. We accomplished this in 1.x using ui-router's templateProvider and controllerProvider.
We will soon be upgrading these applications and need to figure out how to accomplish the equivalent in Angular 2. Is there a better way to do this type of thing?
Reacted by Tomasz, Nguyen Thanh Dat B, Matiss Janis Aboltins, Digital Leader Author, Giulianno Sbrugnera, Dmitry, makdeniss and Alexander
@bencameron I have exact same use case. Did You solve this?
Is this feature planned? Guards return true/false, maybe something similar that could return component?
@Misiu we ended up just handling the dynamic selection of the component within another component that we route to. For example, instead of routing to FooComponent or BarComponent based on some condition that we determine from the route params, we're routing to a component that we use as a container for the other components -- let's call it ContainerComponent.
Then, within the template for ContainerComponent, we're using a div with an ngSwitch directive on it to choose FooComponent or BarComponent based on the condition.
facing a similiar scenario, other than "not pretty" workarounds I don't see any other solution to the problems.
my scenario -> based on the selection company -> the routing tree differs slightly, and the same url can re-direct to two different components.
It would be really cool if the Angular team comes up with a feature to solve such use-cases.
combineLatest([this.routerEventService.currentUrl$, this.someAsyncServiceYouDependUpon.someAsyncResult$, this.componentInitialized$])
.pipe(
map(([a, b, c]) => ({ url: a, someAsyncResult: b, initialized: c })),
switchMap(e => {
if (e.initialized) {
const returnNumberInHashTable= e.someAsyncResult.returnNumberInHashTable;
const routeData = this.activatedRoute.snapshot.data;
if (returnNumberInHashTable) {
if (returnNumberInHashTable in routeData.keys) {
return of(returnNumberInHashTable); // this structure is required to run this component only once (later you see takeWhile)
}
}
}
return of(undefined);
}),
takeWhile(linkId => linkId !== undefined)
).subscribe(linkId => this.createComponent(linkId));
UPD: the example above is good, but it does not work when you need update routes dynamically. So here is my solution.
The logic of this module is following: for authorized users the subscriptions page must be shown, for guests the news page must be shown.
For this, we need possibility to check some condition on each route visit. The condition must be based on some dynamically changable value - in other words, we need to somehow inject a service.
First goal might be achieved with matcher, second - with useFactory for providers.
Using provider factory allows to inject AuthService needed for conditionally selecting the component we need. With custom url matcher it is possible to re-check isAuthorized flag each time when the route is opened - unlike checking in the mentioned article, which is executed only once.
However, on logging out the route will stay the same. This problem is solved with AuthObserverService.
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { skip } from 'rxjs/operators';
import { AuthService } from 'src/app/services/auth.service';
@Injectable()
export class AuthObserverService {
constructor(
private authService: AuthService,
private router: Router
) {}
private authChangeSubscription: Subscription = this
.authService
.isAuthorized$
.pipe(skip(1))
.subscribe(() => {
if (this.router.url !== '/') {
return;
}
this.router.navigated = false;
this.router.navigate([this.router.url]);
});
public ngOnDestroy(): void {
this.authChangeSubscription.unsubscribe();
}
}
The AuthObserverService subscribes to isAuthorized$ observable (skip needed as in my app the isAuthorized$ is a BehaviorSubject). When the authorization state is updated, the route is reloaded too. if (this.router.url !== '/') { needed to not to trigger page reloading when any other route is active.
Activity
DzmitryShylovich commentedon Oct 5, 2016
Use CanActivate guard and redirect to an appropriate page based on user role.
alexjoverm commentedon Oct 5, 2016
But, doing that means you're redirecting to another route (let's say like "user-dashobard" or "admin-dashboard", while I wanted to keep them under "/dashboard".
Maybe it's just not the way it should be done? In that case, what are the drawbacks or the approach?
pkozlowski-opensource commentedon Oct 5, 2016
IMO it would be super-confusing to have 2 completely different pages under the same URL. Imagine that someone who is an admin copies the
/dashboardURL and sends it over to someone else that isn't. The other person would be able to access the page but would see completely different content from the intended one!DzmitryShylovich commentedon Oct 5, 2016
I know it's a limitation but there're a lot of real bugs in the router and I don't think requested feature will be implemented in the nearest future.
alexjoverm commentedon Oct 5, 2016
@pkozlowski-opensource didn't see it in that way, but it makes sense.
amartone commentedon Oct 7, 2016
I've wanted this feature for some time, as well!
bencameron commentedon Oct 11, 2016
I'm in need of this feature too.
For a few different applications we've written in Angular 1.x, we have a route that takes in an itemId as a parameter. The item is then looked up by Id, and then the template/controller is selected based on the itemType. We accomplished this in 1.x using ui-router's templateProvider and controllerProvider.
We will soon be upgrading these applications and need to figure out how to accomplish the equivalent in Angular 2. Is there a better way to do this type of thing?
HorusGoul commentedon May 14, 2017
Any update about this?
prog110 commentedon Jun 20, 2017
@vsavkin Is there any chance of this being implemented sometime soon?
Misiu commentedon Sep 20, 2017
@bencameron I have exact same use case. Did You solve this?
Is this feature planned? Guards return true/false, maybe something similar that could return component?
bencameron commentedon Sep 20, 2017
@Misiu we ended up just handling the dynamic selection of the component within another component that we route to. For example, instead of routing to FooComponent or BarComponent based on some condition that we determine from the route params, we're routing to a component that we use as a container for the other components -- let's call it ContainerComponent.
Then, within the template for ContainerComponent, we're using a div with an ngSwitch directive on it to choose FooComponent or BarComponent based on the condition.
Misiu commentedon Sep 21, 2017
@bencameron than's for reply. For now I'll stick with same solution.
@DzmitryShylovich any chances this could be added in near future?
51 remaining items
rohittidke commentedon Jul 1, 2021
facing a similiar scenario, other than "not pretty" workarounds I don't see any other solution to the problems.
my scenario -> based on the selection company -> the routing tree differs slightly, and the same url can re-direct to two different components.
It would be really cool if the Angular team comes up with a feature to solve such use-cases.
rohittidke commentedon Jul 8, 2021
I found an intermediate solution if it helps anyone and it works very well and is performant.
This is how the route looks like
{ path: 'account', data: { breadcrumb: 'My Account' }, children: [ { path: 'my-account-status', component: InterfaceIdBasedRouting, data: { breadcrumb: 'My Account Status', keys: { 60: MyAccountStatusComponent, 64: OtherMyAccountStatusComponent } } },The InterfaceIdBasedRouting looks like this
`@Component({
selector: 'app-conditional-router',
templateUrl: './linkid-router.component.html'
})
export class InterfaceIdBasedRouting implements AfterViewInit, OnInit {
@ViewChild(PlaceholderDirective) placeholder: PlaceholderDirective;
componentInitialized$ = new BehaviorSubject(false);
constructor(private ComponentFactoryResolver: ComponentFactoryResolver, private someAsyncServiceYouDependUpon: SomeAsyncServiceYouDependUpon, private routerEventService: RouterEventService, private activatedRoute: ActivatedRoute) { }
ngAfterViewInit(): void {
}
ngOnInit(): void {
this.componentInitialized$.next(true);
}
createComponent(linkId: number) {
this.placeholder.ViewContainerRef.clear();
const routeData = this.activatedRoute.snapshot.data;
const componentFactory = this.ComponentFactoryResolver.resolveComponentFactory(routeData.keys[linkId]);
const componentRef = this.placeholder.ViewContainerRef.createComponent(componentFactory);
}
}`
The placeholder directive
`import { Directive, ViewContainerRef } from '@angular/core';
@directive({
selector: '[appPlaceholder]'
})
export class PlaceholderDirective {
constructor(public ViewContainerRef: ViewContainerRef) {
}
}`
ArtemGrachov commentedon Aug 29, 2021
After hours of search, I have found this article: https://www.linkedin.com/pulse/how-make-route-load-different-components-based-async-veloso-alves/ with very simple and clear solution.
UPD: the example above is good, but it does not work when you need update routes dynamically. So here is my solution.
The logic of this module is following: for authorized users the subscriptions page must be shown, for guests the news page must be shown.
For this, we need possibility to check some condition on each route visit. The condition must be based on some dynamically changable value - in other words, we need to somehow inject a service.
First goal might be achieved with
matcher, second - withuseFactoryfor providers.Using provider factory allows to inject AuthService needed for conditionally selecting the component we need. With custom url matcher it is possible to re-check
isAuthorizedflag each time when the route is opened - unlike checking in the mentioned article, which is executed only once.However, on logging out the route will stay the same. This problem is solved with
AuthObserverService.The
AuthObserverServicesubscribes toisAuthorized$observable (skipneeded as in my app theisAuthorized$is aBehaviorSubject). When the authorization state is updated, the route is reloaded too.if (this.router.url !== '/') {needed to not to trigger page reloading when any other route is active.feat(router): Add CanMatch guard to control whether a Route should match
angular-automatic-lock-bot commentedon Jul 14, 2022
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.