angulartypescriptangular-ui-routerprimengangular-routing

Implement routing with steps component in a dynamic dialog priemng


Could anyone help me with implementing a steps component in a dynamic dialog in primeng. I am working on a sample project where I need to implement login screen in a dialog box that includes multiple steps like entering basic information in first step and then entering more details about the user in next steps.

I would like to navigate to different components as I move from one step to another while staying in the dynamic dialog box.

Any general directions is much appreciated. Thank you


Solution

  • I think it is achievable this way:

    Root App component bootstraping:

    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        <h1>Hello from {{ name }}!</h1>
        <router-outlet></router-outlet>
      `,
      imports: [RouterOutlet],
    })
    export class App {
      name = 'Angular';
    }
    
    bootstrapApplication(App, {
      providers: [
        /**
         * DialogService must be provided in the component that is about to open the dialog - the component injector is taken in order to keep router-outlets in the right order
         */
        // DialogService,
        provideAnimations(),
        provideRouter([
          {
            path: '',
            loadComponent: () =>
              import('./components/my-page/my-page.component').then(
                (c) => c.MyPageComponent
              ),
            children: [
              {
                path: '',
                pathMatch: 'full',
                redirectTo: 'step-one',
              },
              {
                path: 'step-one',
                loadComponent: () =>
                  import(
                    './components/my-page/steps/1-step-one/step-one.component'
                  ).then((c) => c.StepOneComponent),
              },
              {
                path: 'step-two',
                loadComponent: () =>
                  import(
                    './components/my-page/steps/2-step-two/step-two.component'
                  ).then((c) => c.StepTwoComponent),
              },
              {
                path: 'step-three',
                loadComponent: () =>
                  import(
                    './components/my-page/steps/3-step-three/step-three.component'
                  ).then((c) => c.StepThreeComponent),
              },
            ],
          },
        ]),
      ],
    });
    

    Style.scss:

    @import 'primeng/resources/themes/lara-light-blue/theme.css';
    @import 'primeng/resources/primeng.css';
    @import 'primeicons/primeicons.css';
    @import 'primeflex/primeflex.css';
    

    Page component that will show the dialog:

    <ng-container
      *ngIf="dynamicDialog; else dialogTpl"
      [ngTemplateOutlet]="dynamicDialogTpl"
    ></ng-container>
    
    <ng-template #dialogTpl>
      <button
        type="button"
        (click)="showDialog()"
        pButton
        icon="pi pi-info-circle"
        label="Show dialog"
        [disabled]="dialogVisible"
      ></button>
      <p-dialog position="top" [(visible)]="dialogVisible">
        <app-steps-dialog></app-steps-dialog>
      </p-dialog>
    </ng-template>
    
    <ng-template #dynamicDialogTpl>
      <button
        type="button"
        (click)="showDynamicDialog()"
        pButton
        icon="pi pi-info-circle"
        label="Show dynamic dialog"
        [disabled]="dynamicDialogVisible"
      ></button>
    </ng-template>
    
    @Component({
      templateUrl: './my-page.component.html',
      standalone: true,
      imports: [
        ButtonModule,
        DialogModule,
        StepsDialogComponent,
        NgIf,
        NgTemplateOutlet,
      ],
      /**
       * DialogService must be provided in the component that is about to open the dialog - the component injector is taken in order to keep router-outlets in the right order
       */
      providers: [DialogService],
    })
    export class MyPageComponent implements OnInit {
      /**
       * a switch between dialog an dynamicDialog
       */
      dynamicDialog = true;
    
      dialogVisible: boolean = true;
      dynamicDialogVisible: boolean = false;
    
      private destroyRef = inject(DestroyRef);
      private dialogService = inject(DialogService);
    
      private dynamicDialogRef: DynamicDialogRef | undefined;
    
      private viewContainerRef = inject(ViewContainerRef);
    
      ngOnInit() {}
    
      showDialog(): void {
        if (!this.dynamicDialog) {
          this.dialogVisible = true;
        }
      }
    
      showDynamicDialog(): void {
        if (this.dynamicDialog) {
          this.dynamicDialogVisible = true;
          this.dynamicDialogRef = this.dialogService.open(StepsDialogComponent, {
            appendTo: this.viewContainerRef.element.nativeElement,
            data: {
              dynamic: true,
            },
          });
          this.dynamicDialogRef.onClose
            .pipe(
              tap(() => {
                this.dynamicDialogVisible = false;
                this.dynamicDialogRef = void 0;
              }),
              takeUntilDestroyed(this.destroyRef)
            )
            .subscribe();
        }
      }
    }
    

    Steps Dialog Component:

    <div class="card">
      <p-steps [model]="items" [readonly]="false"></p-steps>
      <router-outlet></router-outlet>
    </div>
    
    @Component({
      selector: 'app-steps-dialog',
      templateUrl: './steps-dialog.component.html',
      standalone: true,
      imports: [StepsModule, ButtonModule, RouterOutlet],
    })
    export class StepsDialogComponent {
      items: MenuItem[] = [
        { label: 'Step One', routerLink: 'step-one' },
        { label: 'Step Two', routerLink: 'step-two' },
        { label: 'Step Three', routerLink: 'step-three' },
      ];
    
      dynamicDialogConfig = inject(DynamicDialogConfig, { optional: true });
    
      constructor() {
        console.log('dynamicDialogConfig', this.dynamicDialogConfig);
      }
    }
    

    Step One component:

    <h2>Step One</h2>
    <div class="flex justify-content-end">
      <p-button icon="pi pi-chevron-right" routerLink="/step-two"></p-button>
    </div>
    
    @Component({
      templateUrl: './step-one.component.html',
      standalone: true,
      imports: [RouterLink, ButtonModule],
    })
    export class StepOneComponent {}
    

    Step Two component:

    <h2>Step Two</h2>
    <div class="flex justify-content-between">
      <p-button icon="pi pi-chevron-left" routerLink="/step-one"></p-button>
      <p-button icon="pi pi-chevron-right" routerLink="/step-three"></p-button>
    </div>
    
    @Component({
      templateUrl: './step-two.component.html',
      standalone: true,
      imports: [RouterLink, ButtonModule],
    })
    export class StepTwoComponent {}
    

    And Step Three component:

    <h2>Step Three</h2>
    <div class="flex justify-content-start">
      <p-button icon="pi pi-chevron-left" routerLink="/step-two"></p-button>
    </div>
    
    @Component({
      templateUrl: './step-three.component.html',
      standalone: true,
      imports: [RouterLink, ButtonModule],
    })
    export class StepThreeComponent {}
    

    And the last but not least :) stackblitz@demo