5

To start: my StackBlitz contains the exact case I'm trying to solve.

Like the title says I have a routed dialog (dialog that opens based on the route) and inside this dialog I want to have a tab control. Every tab should also be bound to a route, so I think my dialog should somehow also get a <router-outlet/>

But when I add this extra <router-outlet/> without the name argument the rendering (I think) goes frenzy -> result: unresponsive app.

When I add the name argument and I also configure the routing it doesn't work either.

const routes: Routes = [
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent, children: [
    {path: 'dialog', component: DialogWrapperComponent},
    {path: 'routed-dialog',
     children: [
       {path: '', component: RoutedDialogWrapperComponent},
       {path: 'first', component: FirstComponent 
          //, outlet:'test' // Adding this does not work
       }
     ]
    }
  ]},
]

So, the RoutedDialogWrapperComponent looks like this:

import { Component } from "@angular/core";
import { MatDialog } from "@angular/material/dialog";
import { RoutedDialogComponent } from "./routed-dialog.component";
import { Router, ActivatedRoute } from "@angular/router";

@Component({
  template:''
})
export class RoutedDialogWrapperComponent{
  constructor(private dialog:MatDialog, private router:Router,
    private route: ActivatedRoute){
    this.openDialog();
  }

  private openDialog(){
    let dialog = this.dialog.open(RoutedDialogComponent);

    dialog.afterClosed().subscribe(result => {
      this.router.navigate(['../'],
        {
          relativeTo: this.route
        });
    });
  }
}

and the HTML of the RoutedDialogComponent like this:

<nav mat-tab-nav-bar>
  <a mat-tab-link routerLink="/home/dialog2/first">First</a>
  <a mat-tab-link>Second</a>
  <a mat-tab-link>Third</a>
</nav>

<!-- without name it rendering goes wild and will hang -->
<router-outlet name='test'></router-outlet>

<!-- this router-outlet does not work ... -->
<!-- 
  <router-outlet></router-outlet>
-->

In the end I want to have an url like: '/home/routed-dialog/first' and inside the dialog, under the tab-nav, I want to see the FirstComponent but I don't know how to...

Other cases like having the correct tab 'activated' is something I'm confident enough about to fix, but on this one I need some help because my Angular knowledge (as goes for its slang) is pretty limited :-)

321X
  • 3,153
  • 2
  • 30
  • 42

1 Answers1

7

You can find solution with named router outlets here 'router-outlet' in MatDialog is not working in Angular 7

But if you really want to have clean links without strange constructions like /routed-dialog(popupContent:first) then you can do the following trick:

  • define all tabbed components as children of RoutedDialogWrapperComponent:

    {
      path: "routed-dialog",
      component: RoutedDialogWrapperComponent,
      children: [
        {
          path: "first",
          component: FirstComponent
        },
        {
          path: "second",
          component: SecondComponent
        },
        {
          path: "third",
          component: ThirdComponent
        },
        {
          path: '**',
          redirectTo: 'first'
        }
      ],
    }
    
  • put <router-outlet> in template of RoutedDialogWrapperComponent and wrap it with <ng-template>

    <ng-template>
      <router-outlet></router-outlet>
    </ng-template>
    
  • grab reference to that wrapper in component class:

    export class RoutedDialogWrapperComponent {
      @ViewChild(TemplateRef, { static: true }) templateRef: TemplateRef<any>;
    
  • pass that reference to RoutedDialogComponent:

    export class RoutedDialogWrapperComponent implement OnInit {
       ngOnInit() {
         this.openDialog();
       }
    
       private openDialog() {
         const dialog = this.dialog.open(RoutedDialogComponent);
         dialog.componentInstance.contentTemplate = this.templateRef;
         ...
       }
    }
    
  • finally, render it within RoutedDialogComponent:

    <nav mat-tab-nav-bar>
      <a mat-tab-link routerLink="home/routed-dialog/first">First</a>
      <a mat-tab-link routerLink="home/routed-dialog/second">Second</a>
      <a mat-tab-link routerLink="home/routed-dialog/third">Third</a>
    </nav>
    
    <ng-template [ngTemplateOutlet]="contentTemplate"></ng-template>
    

Forked Stackblitz

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Wow, great! I appreciate your assistance very much! What do you feel about such a solution? Is it a good approach in your opinion? I just started developing with Angular but the ViewChild thing feels hacky to me somehow, is that feeling right? – 321X Aug 17 '20 at 10:15
  • Yes, I agree that it looks a bit hacky. But opening dialog in routed component is already hacky solution for opening dialog for specific router. Also, material codebase uses similar approach in some components in order to port `ng-content` to different place https://github.com/angular/components/blob/23d3c216c65b327e0acfb48b53302b8fda326e7f/src/material/tabs/tab.html#L4 – yurzui Aug 17 '20 at 10:20
  • I see... I don't mind the ng-content solution that much though. How would you do it otherwise? Completely move away from the dialog for specific router and open that component in the main view or so? – 321X Aug 17 '20 at 10:25
  • I doesn't matter where you open dialog but router-outlet heavily relies on parent-child context if you break that chain(don't include `` in parent routed component) then only one solution is to use named outlets. – yurzui Aug 17 '20 at 10:39