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

Angular 5 - "Error: Cannot activate an already activated outlet" when named outlets are used in children #20694

Closed
gigadie opened this issue Nov 29, 2017 · 43 comments

Comments

@gigadie
Copy link

gigadie commented Nov 29, 2017

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

As stated here on stackoverflow when you have a routing configuration like:

export const routes = [
{
    path: '',
    component: IndexComponent, 
    children: [
        {
            path: 'home',
            children: [
                { path: '', component: FirstComponent, outlet: 'first', data: { state: 'a' } },
                { path: '', component: SecondComponent, outlet: 'second', data: { state: 'b' } }
            ]
        },
        {
            path: 'about', 
            children: [
                { path: '', component: FirstComponent, outlet: 'first', data: { state: 'b' } },
                { path: '', component: SecondComponent, outlet: 'second', data: { state: 'a' } }
            ]
        }
    ]
}

And you are navigating from /home to /about using a link <a routerLink="['/about']">about</a> it throws the following error:

ERROR Error: Uncaught (in promise): Error: Cannot activate an already activated outlet
Error: Cannot activate an already activated outlet

Expected behavior

The expected behavior would be that it navigates to /about and the router populates the named outlets with the new components (taking internally care of deactivation/activation of outlets).

Minimal reproduction of the problem with instructions

Just a simple app, with routing a named outlets with the configuration above.

What is the motivation / use case for changing the behavior?

It's a show stopper.

Environment


Angular version: 5.0.3


Browser:
- [x] Chrome (desktop) version XX
- [x] Chrome (Android) version XX
- [x] Chrome (iOS) version XX
- [x] Firefox version XX
- [x] Safari (desktop) version XX
- [x] Safari (iOS) version XX
- [x] IE version XX
- [x] Edge version XX
 
Others:

gigadie pushed a commit to gigadie/angular that referenced this issue Nov 30, 2017
…vateWith

Instead of throwing an error, we are now able to deactivate the outlet before activating it again with a new route. This makes routing configuration more flexible when we have named outlets we want to re-use on different routes with nested child routes

Closes angular#20694
@gigadie
Copy link
Author

gigadie commented Nov 30, 2017

I submitted the PR above which enables the use case described in the issue, without throwing an error.

Cheers

gigadie added a commit to gigadie/angular that referenced this issue Nov 30, 2017
…vateWith

Instead of throwing an error, we are now able to deactivate the outlet before activating it again with a new route. This makes routing configuration more flexible when we have named outlets we want to re-use on different routes with nested child routes

Closes angular#20694
@Riobe
Copy link

Riobe commented Dec 18, 2017

We've adopted angular at my place of work, and we're relying on this technique for routing because we don't want to embed router-outlet information into the url. Until this works we'll have to rely on a fork of @angular/router that has the fix in the pull request in it to keep moving forward.

I guess it's a more involved way of +1'ing, but I just want it clear that there are others that need this fix too.

Riobe pushed a commit to Riobe/angular that referenced this issue Dec 18, 2017
@wboevink
Copy link

I've got the same error, applying the suggested fix made it only worse (for me).
The solution was in this case removing the component: IndexComponent, or adding a router outlet to the IndexComponent.

I think throwing the error is still the correct way.

gigadie added a commit to gigadie/angular that referenced this issue Dec 22, 2017
…vateWith

Instead of throwing an error, we are now able to deactivate the outlet before activating it again with a new route. This makes routing configuration more flexible when we have named outlets we want to re-use on different routes with nested child routes

Closes angular#20694
@ngbot ngbot bot added this to the Backlog milestone Jan 23, 2018
@ernestbofill
Copy link

ernestbofill commented Feb 9, 2018

You need to change a little bit the routes. One of the 2 child routes should not be named. And of course the IndexComponent should also have two outlets one named and one without name.

export const routes = [
{
    path: '',
    component: IndexComponent, 
    children: [
        {
            path: 'home',
            children: [
                { path: '', component: FirstComponent, data: { state: 'a' } },
                { path: '', component: SecondComponent, outlet: 'second', data: { state: 'b' } }
            ]
        },
        {
            path: 'about', 
            children: [
                { path: '', component: FirstComponent, data: { state: 'b' } },
                { path: '', component: SecondComponent, outlet: 'second', data: { state: 'a' } }
            ]
        }
    ]
}

@ngbot ngbot bot modified the milestones: Backlog, needsTriage Feb 26, 2018
@daniekpo
Copy link

I applied the suggested fix and it fixed the issue for me. I'm not sure what the implications are though. I hope the Angular team accept the PR or at least let us know what their recommended workaround/fix is.

@nihique
Copy link

nihique commented Apr 4, 2018

We have also same problem and fix above helps, what are the plans for merge?

@sliekens
Copy link
Contributor

I have the same problem but I strongly disagree with replacing the error with a call to deactivate() because that is a bandaid fix that only hides the underlying problem.

A better fix would be changing the router to not call activateWith again if the outlet is already activated.

@fakkeldij
Copy link

Just to be sure does the solution not work without earlier version of Node?
I can't find packages/router/src/directives/router_outlet.ts I only have /router/src/directives/router_outlet.d which is different. I might try to put a second named outlet below the first outlet and then just open an empty component in the first and open the new one in the the other outlet. Is there a way to manually deactivate the outlet in later Node versions?

@JHinW
Copy link

JHinW commented Apr 23, 2018

...Ah, Do not add <route-outlet></route-outlet> to any Structural Directive(such as ngIF), angular cannot deactivate <route-outlet/> if it is not rendered 😆

@sashagrunge
Copy link

sashagrunge commented Apr 25, 2018

Have the configs similar to the follows, it works for me. No errors even without fixes.

app-routing.ts

@NgModule({
  imports: [
    RouterModule.forRoot(
      APP_ROUTES,
      APP_ROUTES_EXTRA_OPTIONS
    ),
    DashboardModule
  ],
  exports: [
    RouterModule
  ],
  providers: []
})

dashboard-routing.ts

const ROUTES: Routes = [
  { path: '', component: MainMenuComponent, outlet: 'menu', pathMatch: 'full' },
  { path: 'dashboard', children: [
    { path: '', component: MainMenuComponent, outlet: 'sectionMenu' },
    { path: 'account', loadChildren: '../account/account.module#AccountModule'}
  ]}
];

acount-routing.ts

const ROUTES: Routes = [
  { path: '', component: AccountMenuComponent, outlet: 'sectionMenu'},
  { path: ':id',
    component: AccountDashboardComponent,
    children: [
      {
        path: 'profile',
        loadChildren: '../account-profile-page#AccountProfilePageModule'
      }
    ]
  }
];

@alexma01
Copy link

solved this issue?

@maximewarnault
Copy link

maximewarnault commented May 16, 2018

Same problem here …
Have you found a solution for this @gigadie ?

@nofear87
Copy link

found a working workaround here: #20712 (comment)

@JrPribs
Copy link

JrPribs commented Jul 10, 2018

@nofear87 FYI for me that fix only fixes the error when routing to the page after the app is loaded. While it does work It does not get rid of the error for a direct route to the child route. Are you seeing that as well?

Any update on a fix for this issue?

@RaschidJFR
Copy link

This guy's got a working answer: https://stackoverflow.com/a/55742986/3000466.
The workaround is deactivating manually the router on each event:

ngOnInit(): void {
    this.router.events.subscribe(e => {
      if (e instanceof ActivationStart && e.snapshot.outlet === "outletname")
        this.outlet.deactivate();
    });

@Local9
Copy link

Local9 commented Sep 12, 2019

This guy's got a working answer: https://stackoverflow.com/a/55742986/3000466.
The workaround is deactivating manually the router on each event:

ngOnInit(): void {
    this.router.events.subscribe(e => {
      if (e instanceof ActivationStart && e.snapshot.outlet === "outletname")
        this.outlet.deactivate();
    });

Amazing that this helped me get what I wanted but I still have a bad design... welp to pre-day patches.

@aakashgarg19
Copy link

aakashgarg19 commented Sep 12, 2019

hi, why no details about the how and where you put your <route-outlet></route-outlet>, which is important because <route-outlet></route-outlet> should be kept in your dom without damage operations like (container.remove, or NgIF directives wrapped).

hope is helps.

Hello, refer to this video for understanding how route config works and where to put the router outlet.
https://www.youtube.com/watch?v=LaIAHOSKHCQ

@aakashgarg19
Copy link

aakashgarg19 commented Sep 12, 2019

In my situation, Angular misactivated a child route, path changed and Angular tried to reactivate it again. The temporary solution is to attach to that problematic router-outlet's activate event, check the activation logic yourself and if the logic fails, call deactivate on the RouterOutlet instance.

<router-outlet #ro="outlet" (activate)="_routerOutletActivated(ro)"></router-outlet>
_router: Router;
_routerOutletActivated(ro: RouterOutlet) {
  if (!_shouldActivate(_router.url))
    ro.deactivate();
}

What method is _shouldActivate ? can you post this also please?

This is sort of a hack and should not be used. Please refer to my answer for resolving this.
#20694 (comment)

@Rlcolli4
Copy link

Rlcolli4 commented Oct 11, 2019

Any update on this? We are having this problem trying to set up our logout/relogin for users and our dialog outlet is having this issue. We are running Angular 8!
Screen Shot 2019-10-11 at 9 38 39 AM

@aakashgarg19
Copy link

Any update on this? We are having this problem trying to set up our logout/relogin for users and our dialog outlet is having this issue. We are running Angular 8!
Screen Shot 2019-10-11 at 9 38 39 AM

#20694 (comment)

@chimung
Copy link

chimung commented Nov 17, 2019

hi, why no details about the how and where you put your <route-outlet></route-outlet>, which is important because <route-outlet></route-outlet> should be kept in your dom without damage operations like (container.remove, or NgIF directives wrapped).
hope is helps.

Hello, refer to this video for understanding how route config works and where to put the router outlet.
https://www.youtube.com/watch?v=LaIAHOSKHCQ

@aakashgarg19 But it seems to not mention how to work with multiple outlet

@Zarepheth
Copy link

@aakashgarg19

My New Structure :-
image

My routing tree already looks like that - but with additional levels of lazy-loading (and the tree configuration is split into separate files at the lazy-load boundaries). And I'm still getting this error with no named outlets and nested primary outlets.

@rodlandA
Copy link

rodlandA commented Feb 6, 2020

Hi, struggled a while with this myself, but found a way to structure my routes so that the issue no longer occurred, have posted an example of my routes below, hope this can help some of you.

const routes: Routes = {
    path: ':pid',
    component: ViewComponent,
    resolve: {
      project: ProjectResolver,
    },
    children: [
      {
        path: '',
        children: [
          {
            path: '',
            component: InfoComponent,
          },
          {
            path: '',
            component: InfoComponent,
            outlet: 'tabcontent',
          },
        ]
      },
      {
        path: 'contract',
        resolve: {
          contracts: ContractsResolver,
        },
        children: [
          {
            path: '',
            component: ContractsComponent
          },
          {
            path: '',
            component: ContractsComponent,
            outlet: 'tabcontent',
          },
        ]
      },
      {
        path: 'contract/:cid',
        resolve: {
          contract: ContractResolver,
        },
        children: [
          {
            path: '',
            component: ContractComponent,
          },
          {
            path: '',
            component: ContractComponent,
            outlet: 'tabcontent'
          }
        ]
      },
    ]
  }

@coroiu
Copy link

coroiu commented Mar 6, 2020

The suggestion above finally solved it for me. It seems like the angular router doesn't deactivate named router outlets if it can't first deactivate an un-named one, even if the un-named router-outlet doesn't exist in the template. As long as it is present in the router configuration, it's gonna work. Ex:

This doesn't work:

export const routes: Routes = [
  { path: '', component: ShellComponent, children: [
    { path: 'child-route', children: [
      { path: '', outlet: 'named-outlet', component: ChildComponent }
    ]},
    { path: '', children: [
      { path: '', outlet: 'named-outlet', component: DefaultComponent }
    ]}
  ]}
];

But this does:

@Component({ template: '' })
export class EmptyComponent {}

export const routes: Routes = [
  { path: '', component: ShellComponent, children: [
    { path: 'child-route', children: [
      { path: '', component: EmptyComponent },
      { path: '', outlet: 'named-outlet', component: ChildComponent }
    ]},
    { path: '', children: [
      { path: '', component: EmptyComponent },
      { path: '', outlet: 'named-outlet', component: DefaultComponent }
    ]}
  ]}
];

@ghost
Copy link

ghost commented Apr 20, 2020

Finally! Adding a primary (unnamed), empty router-outlet to my template completely fixed it. Thank you

@themizzi
Copy link

Having the same problem and trying not to make our routing messier and having to embed multiple outlet state into our urls. The idea in the PR above works for us (Angular 9.0.6). Any chance there could be movement on this so we don't have to track our own fork for a one liner?

@pflugich
Copy link

Finally! Adding a primary (unnamed), empty router-outlet to my template completely fixed it. Thank you

This and deactivating the router-outlets 'manually' on an ActivationStart as suggested in this issue fixed it for me. I have one router-outlet in a BottomSheet, which in my running solution is my primary outlet (unnamed). I'm running angular in v9.0.7, would be nice to know this fixed some day ;)

@aakashgarg19
Copy link

aakashgarg19 commented May 19, 2020

Finally! Adding a primary (unnamed), empty router-outlet to my template completely fixed it. Thank you

This and deactivating the router-outlets 'manually' on an ActivationStart as suggested in this issue fixed it for me. I have one router-outlet in a BottomSheet, which in my running solution is my primary outlet (unnamed). I'm running angular in v9.0.7, would be nice to know this fixed some day ;)

This is sort of a hack and should not be used. Manual deactivation should not be done. If your router config is proper you will not face this issue and will not need this. Please refer to my answer for resolving this.
#20694 (comment)

Basically nested router outlet you can search if still there are some doubts.

@atscott
Copy link
Contributor

atscott commented Jul 10, 2020

Confirmed in v10; Stackblitz repro: https://stackblitz.com/edit/angular-ivy-2xhhaz?file=src%2Fapp%2Fapp.module.ts

@jelbourn jelbourn added P4 A relatively minor issue that is not relevant to core functions and removed severity2: inconvenient labels Oct 1, 2020
atscott added a commit to atscott/angular that referenced this issue Dec 18, 2020
During route activation, a componentless route will not have a context created
for it, but the logic continues to recurse so that children are still
activated. This can be seen here:
https://github.com/angular/angular/blob/362f45c4bf1bb49a90b014d2053f4c4474d132c0/packages/router/src/operators/activate_routes.ts#L151-L158

The deactivation logic does not currently account for componentless routes. This
is particularly a problem when named outlets are children of those
routes because their outlets will never get a deactivation event. It's
not a problem for primary outlets because some primary outlet parent will still
get a deactivation on a changed route.

This commit adjusts the deactivation logic so that if a context cannot
be retrieved for a given route (because it is componentless), we
continue to recurse and deactivate the children using the same
`parentContexts` in the same way that activation does.

Fixes angular#20694
atscott added a commit to atscott/angular that referenced this issue Dec 18, 2020
During route activation, a componentless route will not have a context created
for it, but the logic continues to recurse so that children are still
activated. This can be seen here:
https://github.com/angular/angular/blob/362f45c4bf1bb49a90b014d2053f4c4474d132c0/packages/router/src/operators/activate_routes.ts#L151-L158

The current deactivation logic does not currently account for componentless routes.

This commit adjusts the deactivation logic so that if a context cannot
be retrieved for a given route (because it is componentless), we
continue to recurse and deactivate the children using the same
`parentContexts` in the same way that activation does.

Fixes angular#20694
atscott added a commit to atscott/angular that referenced this issue Dec 18, 2020
During route activation, a componentless route will not have a context created
for it, but the logic continues to recurse so that children are still
activated. This can be seen here:
https://github.com/angular/angular/blob/362f45c4bf1bb49a90b014d2053f4c4474d132c0/packages/router/src/operators/activate_routes.ts#L151-L158

The current deactivation logic does not currently account for componentless routes.

This commit adjusts the deactivation logic so that if a context cannot
be retrieved for a given route (because it is componentless), we
continue to recurse and deactivate the children using the same
`parentContexts` in the same way that activation does.

Fixes angular#20694
atscott added a commit to atscott/angular that referenced this issue Jan 4, 2021
During route activation, a componentless route will not have a context created
for it, but the logic continues to recurse so that children are still
activated. This can be seen here:
https://github.com/angular/angular/blob/362f45c4bf1bb49a90b014d2053f4c4474d132c0/packages/router/src/operators/activate_routes.ts#L151-L158

The current deactivation logic does not currently account for componentless routes.

This commit adjusts the deactivation logic so that if a context cannot
be retrieved for a given route (because it is componentless), we
continue to recurse and deactivate the children using the same
`parentContexts` in the same way that activation does.

Fixes angular#20694
atscott added a commit that referenced this issue Jan 6, 2021
…40196)

During route activation, a componentless route will not have a context created
for it, but the logic continues to recurse so that children are still
activated. This can be seen here:
https://github.com/angular/angular/blob/362f45c4bf1bb49a90b014d2053f4c4474d132c0/packages/router/src/operators/activate_routes.ts#L151-L158

The current deactivation logic does not currently account for componentless routes.

This commit adjusts the deactivation logic so that if a context cannot
be retrieved for a given route (because it is componentless), we
continue to recurse and deactivate the children using the same
`parentContexts` in the same way that activation does.

Fixes #20694

PR Close #40196
@atscott atscott closed this as completed in 13020f9 Jan 6, 2021
@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Feb 6, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.