Angular Material 对话框 - 工厂模式
Angular Material Dialog - Factory Pattern
我需要使用 angular material 对话框组件创建一个 DRY 模态抽象并且想使用工厂函数,创建一个新的 material 对话框:new Modal(component, config, data)
我愿意:
- 处理属性配置的模态“模型”
- 处理对话框打开和关闭的模态“服务”
- 传递到共享模态组件的任何组件
- “动作”按钮可以作为模板传递到模式中
function signature I'd like to use to create these modals/dialogs:
openModal(): void {
const testData = { name: "dummy name", address: "dummy address" };
this.modalService.createModal(
new Modal(
// component to inject:
ChildComponent,
{
// modal options:
title: "the custom title"
},
{
// modal config:
data: { item: testData },
hasBackdrop: false,
autoFocus: false
},
{
// modal action "templates":
actionsRight: componentInstance.instance.modalFooterRightRef,
actionsLeft: componentInstance.instance.modalFooterLeftRef,
}
)
);
}
}
这可以通过 material 对话框完成吗?
这是当前工作的堆栈闪电战:https://angular-dialog-abstraction.stackblitz.io
有一个包含实现的功能模块和一个用于抽象的共享模块。
当前模态:
√ 打开
√ 注入组件
√ 按预期接收数据
x 按预期工作:没有标题或操作按钮。
模态模型
import { Type } from "@angular/core";
import { MatDialogConfig } from "@angular/material/dialog";
import { ModalOptions, ModalTemplates } from "../interfaces/modal";
import { ModalSizeOptions } from "../enums/modal-size-options.enum";
export class Modal {
static defaultModalOptions: ModalOptions = {
cancel: true,
close: false,
footer: true,
size: ModalSizeOptions.MEDIUM,
title: "Attention"
};
static defaultModalConfig: MatDialogConfig = {
data: null,
ariaDescribedBy: null,
ariaLabel: null,
ariaLabelledBy: null,
autoFocus: true,
backdropClass: null,
closeOnNavigation: false,
componentFactoryResolver: null,
direction: "ltr",
disableClose: true,
hasBackdrop: false,
height: "",
id: "",
maxHeight: null,
maxWidth: null,
minHeight: null,
minWidth: null,
panelClass: "",
position: { top: "", bottom: "", left: "", right: "" },
restoreFocus: true,
role: null,
scrollStrategy: null,
viewContainerRef: null,
width: ""
};
static defaultTemplates: ModalTemplates = {
error: null,
header: null,
content: null,
actionsLeft: null,
actionsRight: null
};
public component: Type<any>;
public options = Modal.defaultModalOptions;
public dialog = Modal.defaultModalConfig;
public templates: ModalTemplates;
constructor(
component: Type<any>,
options?: ModalOptions,
dialog?: MatDialogConfig,
templates?: ModalTemplates
) {
this.component = component;
this.options = options
? Object.assign({}, Modal.defaultModalOptions, options)
: Modal.defaultModalOptions;
this.dialog = dialog
? Object.assign({}, Modal.defaultModalConfig, dialog)
: Modal.defaultModalConfig;
this.templates = templates
? Object.assign({}, Modal.defaultTemplates, templates)
: Modal.defaultTemplates;
}
}
模态组件
import { Component, ComponentFactoryResolver, OnInit, Input } from "@angular/core";
import { Modal } from "../../models/modal";
@Component({
selector: "app-modal",
styleUrls: ["./modal.component.css"],
template: `
<div>
<header>
<h1 mat-dialog-title>{{ modal.options.title }}</h1>
<button mat-icon-button mat-dialog-close="true">
<i class="material-icons">clear</i>
</button>
</header>
<mat-dialog-content>
<ng-template #component></ng-template>
</mat-dialog-content>
<div *ngIf="modal.options.footer">
<footer class="flex justify-between w-full">
<div class="modalFooterLeft">
<mat-dialog-actions>
<ng-container
[ngTemplateOutlet]="modal.templates.actionsLeft"
></ng-container>
</mat-dialog-actions>
</div>
<div class="modalFooterRight">
<mat-dialog-actions>
<ng-container
[ngTemplateOutlet]="modal.templates.actionsRight"
></ng-container>
<button
*ngIf="modal.options.cancel"
mat-button
mat-dialog-close
cdkFocusInitial
> Cancel </button>
<button
*ngIf="modal.options.close"
mat-button
mat-dialog-close
> Close </button>
</mat-dialog-actions>
</div>
</footer>
</div>
</div>
`
})
export class ModalComponent implements OnInit {
@Input() public modal: Modal;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
) {}
ngOnInit() {}
}
模态服务
import { Injectable } from "@angular/core";
import { MatDialog, MatDialogConfig } from "@angular/material/dialog";
import { Modal } from "../models/modal";
@Injectable()
export class ModalService {
constructor(public dialog: MatDialog) {}
public createModal(modal?: Modal): any {
return this.openModal(modal.component, modal.dialog);
}
private openModal(component: any, config: MatDialogConfig): any {
return this.dialog.open(component, config);
}
}
将动作按钮作为模板从注入模式组件的 child 组件传递的示例:
<ng-template #actionsRight>
<button
mat-button color="primary"
[disabled]="f.pristine || !f.valid"
[loading]="this.state.loading"
(click)="createItem()"
>Create</button>
</ng-template>
<ng-template #actionsLeft>
<button
mat-button color="primary"
[disabled]="f.pristine || !f.valid"
[loading]="this.state.loading"
(click)="deleteItem()"
>Delete</button>
</ng-template>
非常感谢任何帮助,谢谢。
我注意到您正在直接打开 ChildComponent
。
this.openModal(modal.component, modal.dialog);
如果您想使用 ModalComponent
作为所有动态创建的对话框的基本包装器,那么您应该改为打开该组件。
modal.service.ts
this.dialog.open(ModalComponent, {
...modal.dialog,
data: modal
});
这里我将对话框相关选项传递给 MatDialog 以及整个 Modal
模型作为 data
。此 data
将在 ModalComponent
中通过 DI 提供。
modal.component.ts
export class ModalComponent implements OnInit {
constructor(
...
@Inject(MAT_DIALOG_DATA) public modal: Modal
) {}
现在,我们在 ModalComponent 中,我们可以使用低级 Angular API:
动态创建传递的 ChildComponent
到 <ng-template #component></ng-template>
export class ModalComponent implements OnInit {
@ViewChild("component", { read: ViewContainerRef, static: true })
componentTarget: ViewContainerRef;
actionsLeft: TemplateRef<any>;
actionsRight: TemplateRef<any>;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private inj: Injector,
@Inject(MAT_DIALOG_DATA) public modal: Modal
) {}
ngOnInit() {
const factory = this.componentFactoryResolver.resolveComponentFactory(
this.modal.component
);
const componentRef = this.componentTarget.createComponent(
factory,
null,
Injector.create({
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: this.modal.dialog.data
}
],
parent: this.inj
})
);
之后,您可以在 ChildComponent 中定义为模板的事件操作按钮:
export class ModalComponent implements OnInit {
actionsLeft: TemplateRef<any>;
actionsRight: TemplateRef<any>;
...
ngOnInit() {
...
const componentRef = this.componentTarget.createComponent(
factory,
...
);
this.actionsLeft = componentRef.instance.actionsLeftRef;
this.actionsRight = componentRef.instance.actionsRightRef;
}
}
我需要使用 angular material 对话框组件创建一个 DRY 模态抽象并且想使用工厂函数,创建一个新的 material 对话框:new Modal(component, config, data)
我愿意:
- 处理属性配置的模态“模型”
- 处理对话框打开和关闭的模态“服务”
- 传递到共享模态组件的任何组件
- “动作”按钮可以作为模板传递到模式中
function signature I'd like to use to create these modals/dialogs:
openModal(): void {
const testData = { name: "dummy name", address: "dummy address" };
this.modalService.createModal(
new Modal(
// component to inject:
ChildComponent,
{
// modal options:
title: "the custom title"
},
{
// modal config:
data: { item: testData },
hasBackdrop: false,
autoFocus: false
},
{
// modal action "templates":
actionsRight: componentInstance.instance.modalFooterRightRef,
actionsLeft: componentInstance.instance.modalFooterLeftRef,
}
)
);
}
}
这可以通过 material 对话框完成吗?
这是当前工作的堆栈闪电战:https://angular-dialog-abstraction.stackblitz.io
有一个包含实现的功能模块和一个用于抽象的共享模块。
当前模态:
√ 打开
√ 注入组件
√ 按预期接收数据
x 按预期工作:没有标题或操作按钮。
模态模型
import { Type } from "@angular/core";
import { MatDialogConfig } from "@angular/material/dialog";
import { ModalOptions, ModalTemplates } from "../interfaces/modal";
import { ModalSizeOptions } from "../enums/modal-size-options.enum";
export class Modal {
static defaultModalOptions: ModalOptions = {
cancel: true,
close: false,
footer: true,
size: ModalSizeOptions.MEDIUM,
title: "Attention"
};
static defaultModalConfig: MatDialogConfig = {
data: null,
ariaDescribedBy: null,
ariaLabel: null,
ariaLabelledBy: null,
autoFocus: true,
backdropClass: null,
closeOnNavigation: false,
componentFactoryResolver: null,
direction: "ltr",
disableClose: true,
hasBackdrop: false,
height: "",
id: "",
maxHeight: null,
maxWidth: null,
minHeight: null,
minWidth: null,
panelClass: "",
position: { top: "", bottom: "", left: "", right: "" },
restoreFocus: true,
role: null,
scrollStrategy: null,
viewContainerRef: null,
width: ""
};
static defaultTemplates: ModalTemplates = {
error: null,
header: null,
content: null,
actionsLeft: null,
actionsRight: null
};
public component: Type<any>;
public options = Modal.defaultModalOptions;
public dialog = Modal.defaultModalConfig;
public templates: ModalTemplates;
constructor(
component: Type<any>,
options?: ModalOptions,
dialog?: MatDialogConfig,
templates?: ModalTemplates
) {
this.component = component;
this.options = options
? Object.assign({}, Modal.defaultModalOptions, options)
: Modal.defaultModalOptions;
this.dialog = dialog
? Object.assign({}, Modal.defaultModalConfig, dialog)
: Modal.defaultModalConfig;
this.templates = templates
? Object.assign({}, Modal.defaultTemplates, templates)
: Modal.defaultTemplates;
}
}
模态组件
import { Component, ComponentFactoryResolver, OnInit, Input } from "@angular/core";
import { Modal } from "../../models/modal";
@Component({
selector: "app-modal",
styleUrls: ["./modal.component.css"],
template: `
<div>
<header>
<h1 mat-dialog-title>{{ modal.options.title }}</h1>
<button mat-icon-button mat-dialog-close="true">
<i class="material-icons">clear</i>
</button>
</header>
<mat-dialog-content>
<ng-template #component></ng-template>
</mat-dialog-content>
<div *ngIf="modal.options.footer">
<footer class="flex justify-between w-full">
<div class="modalFooterLeft">
<mat-dialog-actions>
<ng-container
[ngTemplateOutlet]="modal.templates.actionsLeft"
></ng-container>
</mat-dialog-actions>
</div>
<div class="modalFooterRight">
<mat-dialog-actions>
<ng-container
[ngTemplateOutlet]="modal.templates.actionsRight"
></ng-container>
<button
*ngIf="modal.options.cancel"
mat-button
mat-dialog-close
cdkFocusInitial
> Cancel </button>
<button
*ngIf="modal.options.close"
mat-button
mat-dialog-close
> Close </button>
</mat-dialog-actions>
</div>
</footer>
</div>
</div>
`
})
export class ModalComponent implements OnInit {
@Input() public modal: Modal;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
) {}
ngOnInit() {}
}
模态服务
import { Injectable } from "@angular/core";
import { MatDialog, MatDialogConfig } from "@angular/material/dialog";
import { Modal } from "../models/modal";
@Injectable()
export class ModalService {
constructor(public dialog: MatDialog) {}
public createModal(modal?: Modal): any {
return this.openModal(modal.component, modal.dialog);
}
private openModal(component: any, config: MatDialogConfig): any {
return this.dialog.open(component, config);
}
}
将动作按钮作为模板从注入模式组件的 child 组件传递的示例:
<ng-template #actionsRight>
<button
mat-button color="primary"
[disabled]="f.pristine || !f.valid"
[loading]="this.state.loading"
(click)="createItem()"
>Create</button>
</ng-template>
<ng-template #actionsLeft>
<button
mat-button color="primary"
[disabled]="f.pristine || !f.valid"
[loading]="this.state.loading"
(click)="deleteItem()"
>Delete</button>
</ng-template>
非常感谢任何帮助,谢谢。
我注意到您正在直接打开 ChildComponent
。
this.openModal(modal.component, modal.dialog);
如果您想使用 ModalComponent
作为所有动态创建的对话框的基本包装器,那么您应该改为打开该组件。
modal.service.ts
this.dialog.open(ModalComponent, {
...modal.dialog,
data: modal
});
这里我将对话框相关选项传递给 MatDialog 以及整个 Modal
模型作为 data
。此 data
将在 ModalComponent
中通过 DI 提供。
modal.component.ts
export class ModalComponent implements OnInit {
constructor(
...
@Inject(MAT_DIALOG_DATA) public modal: Modal
) {}
现在,我们在 ModalComponent 中,我们可以使用低级 Angular API:
动态创建传递的ChildComponent
到 <ng-template #component></ng-template>
export class ModalComponent implements OnInit {
@ViewChild("component", { read: ViewContainerRef, static: true })
componentTarget: ViewContainerRef;
actionsLeft: TemplateRef<any>;
actionsRight: TemplateRef<any>;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private inj: Injector,
@Inject(MAT_DIALOG_DATA) public modal: Modal
) {}
ngOnInit() {
const factory = this.componentFactoryResolver.resolveComponentFactory(
this.modal.component
);
const componentRef = this.componentTarget.createComponent(
factory,
null,
Injector.create({
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: this.modal.dialog.data
}
],
parent: this.inj
})
);
之后,您可以在 ChildComponent 中定义为模板的事件操作按钮:
export class ModalComponent implements OnInit {
actionsLeft: TemplateRef<any>;
actionsRight: TemplateRef<any>;
...
ngOnInit() {
...
const componentRef = this.componentTarget.createComponent(
factory,
...
);
this.actionsLeft = componentRef.instance.actionsLeftRef;
this.actionsRight = componentRef.instance.actionsRightRef;
}
}