22

I am using angular 2.0 final. I am trying to change the location of the router-outlet in the main app.component.html. The template is updating fine display wise except, the first time I use router.navigate the component won't display in the new router-outlet, and there is no error. The second and every time after I use router.navigate it works properly.

example template of app.component.html

   <div *ngIf="authenticated() == false">
      <h1>not logged in</h1>
      <router-outlet>
      </router-outlet>
    </div>
    <div *ngIf="authenticated()">
      <h1>logged in</h1>
      <router-outlet>
      </router-outlet>
    </div>
Optimizal
  • 246
  • 1
  • 2
  • 5

6 Answers6

40

I'd like to say please use a named router-outlet which work perfectly fine, but issue for me is that such urls are not user friendly at all and I personally believe url with outlet name does not make sense,

ex:-

route

{ path : "forgotPassword", component :ForgotPassword , outlet : "notlogedin" }

output in browser address bar

http://localhost:4200/(notlogedin:forgotPassword)

see how stupid that it looks in the addressbar.

so if you try to use *ngIf to conditionally disable and enable router-outletto overcome the problem it does not work. One router-outlet will get registered and no matter what you do, next router-outlet will not respond to the route changes.

So Here is The Solution

Using ngTemplateOutlet and ng-template we can provide a good solution to this problem by keeping only one router-outlet see below sample code.

<ul>
  <li><a routerLink="/login">login</a></li>
  <li><a routerLink="/dashboard">dashboard</a></li>
</ul>
    
<!-- This is where my before login router stays -->
<div class="loggedIn-route" *ngIf="routerActive">
  <ng-container *ngTemplateOutlet="template"></ng-container>
</div>
    
<!-- This is where my after login router stays -->
<div class="loggedout-route" *ngIf="!routerActive">
  <ng-container *ngTemplateOutlet="template"></ng-container>
</div>
    
<ng-template #template>
  <router-outlet></router-outlet>
</ng-template>

Try the fiddle

https://jsfiddle.net/imal/e4tyqv95/36/

PaulBunion
  • 346
  • 2
  • 18
Imal Hasaranga Perera
  • 9,683
  • 3
  • 51
  • 41
  • 2
    Very helpful in a situation where I had to move an outlet depending on if you're in desktop or mobile. – Moo Aug 07 '18 at 21:53
  • 1
    Unfortunately, after trying this out, only one of the routers seems to actually respond to route changes. – Moo Aug 07 '18 at 22:53
  • Let me try to provide you a sample – Imal Hasaranga Perera Aug 08 '18 at 04:22
  • 2
    In combination with async auth guards this can cause the behavior that ngOnDestroy is called without calling ngOnInit like i am described here: https://stackoverflow.com/a/58394473/7662651 – Markus Hettich Oct 15 '19 at 15:08
  • To further define what Markus means: when the `*ngIf` value changes on a route change, the `router-outlet` gets rendered instantly instead of waiting for the asyn guard. You can fix that by putting the `*ngIf` value change inside a `setTimeout`. Normally I wouldn't advice using that method but in this case I think there is no other way to fix that behaviour. – Tom Jan 24 '22 at 12:42
11

You should consider using named router-outlet, instead.

It states in the documentation: A template may hold exactly one unnamed .

<div *ngIf="authenticated() == false">
      <h1>not logged in</h1>
      <router-outlet name="notloggedin">
      </router-outlet>
    </div>
    <div *ngIf="authenticated()">
      <h1>logged in</h1>
      <router-outlet name="loggedin">
      </router-outlet>
    </div>

The router will look like:

{ path: 'page1', component: Page1Component, outlet: 'notloggedin' },
{ path: 'page2', component: Page2Component, outlet: 'loggedin' }

Here an example from @yurzui in this post.

Community
  • 1
  • 1
Edmar Miyake
  • 12,047
  • 3
  • 37
  • 38
3

I had to use ViewContainerRef so that both mobile and desktop would leverage the same router outlet:

<!-- MOBILE -->
<div *ngIf="isMobile">
  <div #mobileOutlet></div>
</div>

<!-- DESKTOP -->
<div *ngIf="!isMobile">
  <div #desktopOutlet></div>
</div>

<ng-template #tpl>
  <router-outlet></router-outlet> 
</ng-template>

And I had no problem using createEmbeddedView for both:

@ViewChild('mobileOutlet', { read: ViewContainerRef }) _vcrMobile;
@ViewChild('desktopOutlet', { read: ViewContainerRef }) _vcrDesktop;
@ViewChild('tpl') tpl;

ngAfterViewInit() {
  if (this.isMobile) this._vcrDesktop.createEmbeddedView(this.tpl);
  else this._vcrMobile.createEmbeddedView(this.tpl);
}

only thing is that you'll have to toggle this outlet if you switch between breakpoints. For example, from desktop to mobile resize:

this._vcrDesktop.clear();
this._vcrMobile.createEmbeddedView(this.tpl)
Nate May
  • 3,814
  • 7
  • 33
  • 86
3

You can not use multiple router outlets in the same template with ngIf condition. But you can use it in the way shown below:

<div *ngIf="loading">
  <span>loading true</span>
  <ng-container *ngTemplateOutlet="template"></ng-container>
</div>

<div *ngIf="!loading">
  <span>loading false</span>
  <ng-container *ngTemplateOutlet="template"></ng-container>
</div>



<ng-template #template>
  <router-outlet> </router-outlet>
</ng-template>
PaulBunion
  • 346
  • 2
  • 18
dev0201
  • 73
  • 4
  • Would work but the components rendered in outlet would be called twice on reload. Had to rearrange code because of this bug – Yousaf Raza Apr 01 '22 at 11:54
1

I've had the same problem wherein I have multiple <router-outlet> in my app.component.html and expected them to be in/visible with *ngIf. I've tried almost everything I can find online (some worked but breaks the principle of SPA of angular).

This totally worked for me, and I think is the most logical and pretty simple solution.

<div *ngIf="authenticated">
  <ng-container *ngTemplateOutlet="template"></ng-container>
</div>

<div *ngIf="!authenticated">
  <ng-container *ngTemplateOutlet="template"></ng-container>
</div>

<ng-template #template>
  <router-outlet></router-outlet>
</ng-template>
  • When I click the "Run code snippet" button, I don't see anything happening. Is this because there is a fault in your code, or is it because your code isn't actually a runnable snippet? If the latter, please use code fences instead of making a snippet; if it's the former, then please address the problem. – Adrian Mole Aug 07 '21 at 16:13
-1

User Routing like this, it will solve the problem for named outlet

{
    path: 'login',
    children: [
      {
        path: '',
        component: LoginComponent,
        outlet: 'loutlet',
      }
    ]
  },
  {
    path: 'profile',
    component: ProfileComponent,
  }
ricky
  • 1,644
  • 1
  • 13
  • 25