本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Angular-Material的弹窗在我近几个月的使用中,有了一些心得和经验,故作此记录。
弹窗的使用步骤是:触发弹窗的按钮/文字 => 显示弹窗 => 点击弹窗的按钮触发弹窗关闭 => 触发API
下面以我已经写好的弹窗作为例子
使用场景:点击弹窗,选择交货日期提交,通过比较交货日期和计划日期判断是否延期,如果延期需要提交理由,未延期则不需要。
弹窗的HTML
<h3 mat-dialog-title *ngIf="action == 'submit' || action == 'beyond'">填写交期</h3>
<div mat-dialog-content *ngIf="action == 'submit' || action == 'beyond'">
<p>
<span class="main_text">{{dateTitle}}:{{RealDate | date:"yyyy-MM-dd"}}</span>
</p>
<div>
<span>{{dateLabel}}:</span>
<mat-form-field class="mx24 time_picker">
<input matInput [matDatepicker]="startPicker" [(ngModel)]="postDate" disabled>
<mat-datepicker-toggle matSuffix [for]="startPicker"></mat-datepicker-toggle>
<mat-datepicker #startPicker disabled="false"></mat-datepicker>
</mat-form-field>
</div>
<div class="beyond">
<span>延期理由:</span>
<mat-form-field class="example-full-width">
<input matInput placeholder="" value="" [(ngModel)]="form.comments">
</mat-form-field>
</div>
</div>
<div mat-dialog-actions class="button_box">
<button mat-button (click)="onNoClick()">取消</button>
<button *ngIf="action == 'submit'" mat-button (click)="submitDate()">提交</button>
<button *ngIf="action == 'beyond'" mat-button (click)="beyondDate()">延期</button>
</div>
该HTML的逻辑是通过aciton传递参数,决定弹窗的标题、内容和按钮选择,不同的按钮对应不同的函数用于处理不同的API
两种函数处理API的两种方式
第一种:在弹窗内处理
思路:将参数传给弹窗后,在弹窗中将数据传给API进行处理,处理完成后关闭弹窗。
async beyondDate(): Promise<void> {
const alertTitleText = '注意';
const alertLabelText = '';
const tipsData: any[] = [];
const alertData: DialogData = {
alertTitle: alertTitleText,
alertLabel: alertLabelText,
tips: tipsData,
};
const PostDate = this.general.formatTimeToDate(this.postDate);
const RealDate = this.general.formatTimeToDate(this.RealDate);
if (this.postDate == null || this.postDate === '') {
tipsData.push('未输入' + this.dateLabel);
this.openWarning(alertData);
} else if (this.form.comments === '') {
tipsData.push('请输入延期理由');
this.openWarning(alertData);
} else if (PostDate < RealDate) {
tipsData.push(this.dateLabel + '需要大于' + this.dateTitle);
this.openWarning(alertData);
} else {
const url = `/plan/beyondApply`;
const res = await this.confSvc.sendPostCatch(url,
{
comments: `原预计成品入库日期:${this.general.formatTimeToDate(RealDate)},现更新预计入库日期:${this.general.formatTimeToDate(PostDate)},延期理由:${this.form.comments}`,
plan_id: this.planId,
order_id: this.orderId,
time: this.general.formatTimeToDate(this.postDate),
time_type: this.Status
});
this.confSvc.handleTip(res['msg']);
this.confSvc.changeLoading(false);
this.dialogRef.close();
}
}
该部分的逻辑顺序为:
- 获取弹窗的信息
- 将交货时间与计划时间相比
- 若交货时间>计划时间且未填延期理由,则弹窗(另一个弹窗)必须填写延期理由
- 若交货时间>计划时间且填了延期理由,则直接调用API
- 若交货时间<计划时间,则直接调用API 注意:这里的逻辑是处理完成后,直接调用API,处理完成后关闭弹窗。
第一种方式的优缺点
优点:代码逻辑清晰,从弹窗弹出到弹窗处理结束,一条直线。
缺点:
- 由于material弹窗独立于页面之外,所以无法使用加载图覆盖弹窗作为防抖。
- 弹窗使用的功能变多后,代码会变得更多,不宜维护
第二种方法:弹窗关闭后传值回页面
思路:将参数传给弹窗后,处理好数据保存为一个数组,在弹窗关闭后传回页面,在页面中进行API处理。
form = {
comments: '',
down: {
type: '',
value: ''
}
};
// 完结和保存
async endOrder(): Promise<void> {
const Form = this.form;
const alertTitleText = '注意';
const alertLabelText = '必须填写理由!';
const tipsData: any[] = [];
const alertData: DialogData = {
alertTitle: alertTitleText,
alertLabel: alertLabelText,
tips: tipsData,
};
if (trim(this.form.comments) == '' || trim(this.form.comments) == null) {
this.openWarning(alertData);
} else {
this.dialogRef.close({ Form });
}
}
其中this.dialogRef.close({ Form });代表弹窗关闭后将Form传回页面
处理API的页面ts部分:
// 强制完结
async End(): Promise<void> {
const _action = 'invalid';
……
const dialogRef = this._matDialog.open(DialogOverviewExampleDialogComponent, {
data: {
action: _action,
dateTitle: dateTitleText,
dateLabel: dateLabelText,
RealDate: RealDateText,
order_status: this.orderData['order_status'],
planId: this.planId,
orderId: this.orderId,
order_type: this.isType,
userCnName: userCnName
}
});
dialogRef.afterClosed().subscribe(async (res) => {
if (res && res.Form) {
this.confSvc.changeLoading(true);
const userId = this.general.getUserId();
const statusRes = await this.confSvc.sendPutCatch('order/check', {
plan_id: this.planId,
order_id: this.orderId,
order_status: 100,
is_compel: 1,
comment_type: res.Form.down.value,
comments: userCnName + '强制完结了该订单,原因:' + res.Form.down.type + ',备注:' + res.Form.comments,
products_sku: this.orderData['products_sku'],
});
this.confSvc.handleTip(statusRes['msg']);
this.fetchTableInfo();
if (statusRes['code']) {
this.orderData.order_status = 100;
this.comments.getNoteList();
}
this.confSvc.changeLoading(false);
}
});
}
其中dialogRef.afterClosed()代表弹窗结束后执行的动作。
subscribe是rxjs中一个发布订阅的模式,用于处理弹窗结束后返回的值,比较复杂,还在学习中,只能使用基本用法,等以后学习的差不多了再写一篇相关的博客。
第二种方式的优缺点
优点:
- 弹窗代码并不会很多,且可以对传参写一个对应的函数用于处理,这样所有的弹窗传参均可以通过一个函数整合为数组传回页面(该函数以后再写)
- 代码逻辑易于维护,每个按钮绑定的函数直接在当前页面即可维护,而不需要跳转到弹窗组件中一一寻找。
缺点:写法比较复杂,且牵扯到
rxjs的subscribe,上手难度偏高。