关于前端:记一个angular在路由配置中管理-Angular-Material-Dialog实现动态组件的弹窗显示

34次阅读

共计 4735 个字符,预计需要花费 12 分钟才能阅读完成。

背景形容

指标

咱们的指标正如题目所言:在路由配置中治理 Angular Material Dialog,从而更简便(代码量更少,可复用性,可拓展性、可维护性更强)地实现动静组件的弹窗显示。不难看出,咱们的指标由两个局部组成,动静组件和弹窗。其实二者的独自实现都并不艰难,angular 官网所举荐的泛滥 UI 库中都有二者对应的 demo。然而,要将二者强壮地联合起来并不是一件容易的事件。

我已经尝试过两种计划:一是 bootstrap->model 实现弹窗 +Material->Cdk->Portal 实现动静组件;二是 Material->Dialog 实现弹窗 + 向 openDialog()办法中传参数以管制要加载的组件实现动静组件。对两种计划通过尝试后会发现,两个计划都有问题且大同小异:对应用的组件(C 层、V 层)代码侵入过多,且局部代码须要屡次复写,这对于开发而言并不是好的景象。

起初通过老师疏导,最终发现以路由配置去治理 Material Dialog 实现动静组件弹窗显示的计划更为适合。这就是咱们上面所要钻研的。

Material Dialog 应用办法

首先咱们须要对 Material->Dialog 根底的应用办法有肯定的理解,具体详情请参考官网文档:
https://material.angular.io/components/dialog/overview,在此就不过多赘述。

如何实现以路由配置去治理

实现思路

首先咱们要了解好官网提供的 dialog 的经典用法,经察看和钻研后咱们能够看出它的大体运作流程,如下图:

此流程中呈现上述问题的中央次要集中在第三步骤。因为 openDialog()办法是间接写在组件 C 层中的,这就导致代码入侵这个问题无奈防止。而假使以向办法中传参数来管制要加载的组件实现动静组件的形式,这无疑会让本就侵入的代码量变得更多了,而且还有局部代码须要屡次复写;最最让人受不了的还是:每个想要这样用的组件,都得来上这么一套,切实折磨。所以当初问题进一步转化成了:如何在 Material->Dialog 经典用法的根底上,将本来须要写在组件 C 层中的 openDialog()办法抽离进去,变得可复用,易保护。

经老师领导和上网查阅材料,最终以路由配置去治理的形式实现了咱们想要的成果,再回过头来总结它具体的实现思路:V 层 button 不再间接绑定 openDialog()办法,而是绑定路由 (routerLink) 且要加上路由内容输入语句 (<router-outlet></router-outlet>);在 routing.module 文件中给所绑定子路由的 component 设置为 DialogEntryComponent(翻译过去为弹窗入口组件,没错这是咱们新建的 dialog-entry.component.ts 文件,这时候须要将 openDialog() 办法及其他相干办法从原组件 C 层中移动到这外面来,这样原组件 C 层将不会呈现代码侵入的问题);而后在 routing.module 文件中给所绑定路由的 data 中设置 component: 你想要弹出组件。具体流程如下图:

这样做,将实现弹窗的成果的 openDialog()办法及其他相干办法抽离到 DialogEntryComponent 中;将组件的动静渲染交由路由文件中向 DialogEntryComponent 传入指标组件来实现。完满解决了上文中提到的一系列问题。

代码实现

文件目录筹备:1.Term 目录下须要有 term-index 组件,term-add 组件,term-edit 组件,每个组件中都有 C 层、V 层、CSS 文件、测试文件;还须要有 term.module.ts、term-routing.module.ts 文件。2.dialog-entry 目录下须要有 dialog-entry.component.ts、dialog-entry.module.ts 文件。

term-index 的 C 层什么都不须要

term-index 的 V 层

<!-- 基于 Material Dialogs 的弹窗显示动静组件 demo-->
<button mat-raised-button routerLink="add">add</button>
<button mat-raised-button routerLink="edit/3">edit</button>
<router-outlet></router-outlet>

term-add 的 C 层

export class TermAddComponent implements OnInit {
  constructor(public dialogRef: MatDialogRef<TermAddComponent>,) {}
  ngOnInit(): void {}
  onNoClick(): void {this.dialogRef.close();
  }
  onOkClick(): void {this.dialogRef.close();
  }
}

term-add 的 V 层

<h1 mat-dialog-title>Hi! term-add works</h1>
<div mat-dialog-actions>
  <button mat-button (click)="onNoClick()">No Thanks</button>
  <button mat-button (click)="onOkClick()" cdkFocusInitial>Ok</button>
</div>

term-edit 的 C 层与 term-add 的基本相同

只需将构造函数中 MatDialogRef<> 填入本人的组件即可。

term-edit 的 V 层与 term-add 的基本相同

只需将 h1 标签中填入可辨识此组件的内容即可。

term-routing.module.ts

import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {TermIndexComponent} from './term-index/term-index.component';
import {TermAddComponent} from './term-add/term-add.component';
import {TermEditComponent} from './term-edit/term-edit.component';
import {DialogEntryComponent} from '../../common/dialog-entry/dialog-entry.component';

const routes: Routes = [
  {
    path: '',
    component: TermIndexComponent,
    children: [
      {
        path: 'add',
        component: DialogEntryComponent,
        data: {component: TermAddComponent}
      },
      {
        path: 'edit/:TermId',
        component: DialogEntryComponent,
        data: {component: TermEditComponent}
      }
    ]
  }
];

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

dialog-entry.component.ts

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

@Component({template: ''})
export class DialogEntryComponent {
  url: string | undefined;
  constructor(public dialog: MatDialog,
              private router: Router,
              private route: ActivatedRoute) {this.url = this.route.snapshot.url[0].path;
    this.openDialog();}
  openDialog(): void {
    const dialogRef = this.dialog.open(this.route.snapshot.data.component, {width: '250px'});
    const relativeBackUrl = this.getRelativeBackUrl();
    dialogRef.afterClosed().subscribe(result => {this.router.navigate([relativeBackUrl], {relativeTo: this.route});
    });
  }

  private getRelativeBackUrl(): string {if (this.url === 'add') {return '../';} else if (this.url === 'edit') {return '../../';} else {return '';}
  }
}

dialog-entry.module.ts

import {NgModule} from '@angular/core';
import {MatDialogModule} from '@angular/material/dialog';
import {DialogEntryComponent} from './dialog-entry.component';

@NgModule({
  declarations: [DialogEntryComponent],
  imports: [MatDialogModule],
})
export class DialogEntryModule {
}

最初在应用时须要记得在模块中引入 DialogEntryModule。此 demo 为 term 模块则有

term.module.ts

@NgModule({declarations: [TermIndexComponent, TermAddComponent, TermEditComponent],
  imports: [
    CommonModule,
    TermRoutingModule,
    ReactiveFormsModule,
    MatButtonModule,
    DialogEntryModule,
    MatDialogModule
  ],
  providers: [{ provide: MatDialogRef, useValue: {} },
  ],
})

总结

至此,咱们的指标终于达成了,之后其余模块想要复用的话,只须要做以下 3 项工作即可:
1.在 module 文件中 importers 咱们的 DialogEntryModule;
2.给主组件的 V 层 button 中绑定转跳的路由信息;
3.在 routing.module 文件中设置对应路由所要转跳的组件。

家喻户晓,第 2 第 3 步工作就算不应用动静组件弹窗也是无奈防止的。而像本文这般配置好后,之后其余模块再想要复用时,多出的工作内容只有第 1 步和第 3 步中多写几行简略的代码而已。这就是咱们想要的,不必造反复的轮子,以及能够在复用起来轻松而简便。

成果预览

参考文章

https://medium.com/ngconf/routing-to-angular-material-dialogs…

正文完
 0