Modal Dialog Route in Angular

The other day I was working on a pet project in Angular.

One of the things I wanted to achieve was to have a route which will show a popup. In simple words the case looks the following: the user is on a main page https://pet-project.com/, then navigates to https://pet-project.com/create through a link etc. and gets a popup dialog on top of a main page.

Showing one page on top of the other can be simply done through child routes (make sure your parent view has a router-outlet to show child components):

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { FeedComponent } from './feed/feed.component';
import { DialogComponent } from './dialog/dialog.component';

const routes: Routes = [
  {
    path: '',
    component: FeedComponent,
    children: [
      { path: 'create', component: DialogComponent },
    ]
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Where the DialogComponent will spin up a CreateComponent and navigate back once closed:

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
import { CreateComponent } from './create/create.component';

@Component({ template: '' })
export class DialogComponent implements OnInit {
  constructor(private dialog: MatDialog) { }

  ngOnInit() {
    const dialogRef = this.dialog.open(CreateComponent, {});
    dialogRef.afterClosed().subscribe(_ => this.router.navigate(['..'], { relativeTo: this.route }));
  }

So linking modals to routes was pretty easy to achieve.

Things got more interesting when I tried to generalize the DialogComponent to work with any child component. Coming from a C# world, I thought it will be a piece of cake with generics, just by using DialogComponent<ChildComponent>.

After updating the route to something like this:

{ path: 'create', component: DialogComponent<CreateComponent> }

But I got an error:

Value of type 'typeof DialogComponent' is not callable. Did you mean to include 'new'?

Investigating it a bit led me to InjectionToken. I will skip the details as it is not a part of the final solution.

The next one was making the DialogComponent generic then calling

this.dialog.open(T, {});

And because generics in TypeScript are compile-time only, this did not work either, throwing the other error at me:

'T' only refers to a type, but is being used as a value here.

Fighting with the issue for a few hours did not push me much towards a solution.

So I took a step back and decided to use parameterized routes instead of generics. So we would pass a child component as a route parameter:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { FeedComponent } from './feed/feed.component';
import { UploadComponent } from './upload/upload.component';
import { DialogComponent } from './dialog/dialog.component';

const routes: Routes = [
  {
    path: '',
    component: FeedComponent,
    children: [
      { path: 'upload', component: DialogComponent, data: { component: UploadComponent } }
    ]
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

And the final version of DialogComponent will be like this:

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, Data } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';

@Component({ template: '' })
export class DialogComponent implements OnInit {
  constructor(
    private dialog: MatDialog,
    private router: Router,
    private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.data.subscribe(data => this.openDialog(data));
  }

  openDialog(data: Data): void {
    const dialogRef = this.dialog.open(data.component, {});
    dialogRef.afterClosed().subscribe(_ => this.router.navigate(['..'], { relativeTo: this.route }));
  }
}

Hope this will save you few hours of a hustle.

Happy routing!

Comments

  1. Angular bloggers are content creators and software developers who share knowledge, tutorials, best practices, and updates related to the Angular framework. Through blogs, articles, and technical guides, they help developers learn concepts such as components, services, dependency injection, routing, and state management. Their content ranges from beginner-friendly introductions to advanced topics, making Angular development more accessible to a wide audience. Many Angular bloggers also review new framework releases and explain how developers can adopt the latest features effectively.

    ReplyDelete
  2. The contributions of Angular bloggers play an important role in the growth of the Angular community. By sharing practical examples, troubleshooting tips, and real-world project experiences, they help developers solve problems more efficiently and improve their coding skills. Web Development ProjectsTheir blogs often encourage discussion and collaboration, allowing readers to exchange ideas and stay informed about industry trends. As a result, Angular bloggers serve as valuable educational resources for both aspiring and experienced web developers.

    ReplyDelete

Post a Comment