尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Ionic上拉菜单开发实战:跨平台UI组件全解析

Ionic上拉菜单开发实战:跨平台UI组件全解析 1. Ionic上拉菜单实战指南从原理到实现的完整解决方案移动应用开发中交互设计的重要性不言而喻。上拉菜单Action Sheet作为一种常见的UI组件在iOS和Android平台上都有广泛应用。Ionic框架提供的上拉菜单组件不仅保持了原生体验还能轻松实现跨平台一致性。本文将深入解析Ionic上拉菜单的实现原理并通过完整案例演示如何在实际项目中灵活运用。2. 上拉菜单的核心价值与适用场景2.1 为什么选择上拉菜单上拉菜单在移动UI设计中扮演着重要角色它能在有限屏幕空间内优雅地组织次级操作。相比传统下拉菜单上拉菜单更符合移动设备底部操作的热区特性用户单手操作时触达率更高。根据Material Design的FAB浮动操作按钮设计规范上拉菜单是扩展主要操作的自然延伸。在Ionic应用中上拉菜单特别适合以下场景需要从多个相关操作中选择一项如分享到不同平台执行潜在破坏性操作前的确认如删除内容展示与当前上下文相关的附加功能需要保持界面简洁时的操作收纳2.2 Ionic上拉菜单的独特优势Ionic框架的Action Sheet组件具有以下特点跨平台一致性自动适配iOS和Android的设计语言动画流畅内置符合平台特性的过渡动画高度可定制支持图标、颜色、按钮排列等深度定制易用API通过简单方法调用即可触发和控制3. 基础实现快速创建第一个上拉菜单3.1 环境准备与组件导入确保已创建Ionic项目并安装核心依赖。在需要使用上拉菜单的页面或组件中首先导入Action Sheet控制器import { ActionSheetController } from ionic/angular; constructor(private actionSheetCtrl: ActionSheetController) {}3.2 基本配置与触发方法创建一个基础的异步方法来生成和呈现上拉菜单async presentActionSheet() { const actionSheet await this.actionSheetCtrl.create({ header: 操作选项, buttons: [ { text: 删除, role: destructive, icon: trash, handler: () { console.log(删除操作触发); } }, { text: 分享, icon: share, handler: () { console.log(分享操作触发); } }, { text: 取消, icon: close, role: cancel, handler: () { console.log(操作取消); } } ] }); await actionSheet.present(); }3.3 模板绑定与事件触发在HTML模板中添加触发按钮ion-button (click)presentActionSheet() expandblock 显示操作菜单 /ion-button4. 高级定制技巧与实战经验4.1 多平台样式适配策略Ionic的Action Sheet会自动根据运行平台应用不同的样式但我们也可以进行深度定制const actionSheet await this.actionSheetCtrl.create({ cssClass: custom-action-sheet, // 自定义CSS类 mode: md, // 强制使用Material Design样式 // 其他配置... });对应的全局SCSS样式.custom-action-sheet { --button-color: #3880ff; --background: #f4f5f8; .action-sheet-title { font-weight: bold; } .action-sheet-button.ion-focused { background-color: rgba(56, 128, 255, 0.1); } }4.2 动态按钮生成与条件渲染实际项目中菜单项往往需要动态生成。以下是结合业务逻辑的实践async presentDynamicActionSheet() { const user await this.authService.getCurrentUser(); const buttons []; // 基础操作 buttons.push({ text: 查看详情, icon: eye, handler: () this.viewDetails() }); // 管理员专属操作 if (user.role admin) { buttons.push({ text: 管理设置, icon: settings, handler: () this.openAdminSettings() }); } // 添加取消按钮 buttons.push({ text: 取消, icon: close, role: cancel }); const actionSheet await this.actionSheetCtrl.create({ header: 请选择操作, buttons }); await actionSheet.present(); }4.3 复杂交互与状态管理当上拉菜单需要与组件状态交互时推荐使用RxJS进行响应式管理import { Subject } from rxjs; import { takeUntil } from rxjs/operators; private destroy$ new Subjectvoid(); async presentInteractiveActionSheet() { const actionSheet await this.actionSheetCtrl.create({ header: 排序方式, buttons: [ { text: 按日期排序, handler: () this.sortBy(date) }, { text: 按名称排序, handler: () this.sortBy(name) }, { text: 取消, role: cancel } ] }); await actionSheet.present(); // 监听菜单关闭事件 actionSheet.onDidDismiss() .pipe(takeUntil(this.destroy$)) .subscribe(data { console.log(菜单关闭原因:, data.role); this.updateViewState(); }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }5. 性能优化与最佳实践5.1 内存管理与组件销毁不当的上拉菜单管理可能导致内存泄漏。确保在Angular组件销毁时正确处理private actionSheet: HTMLIonActionSheetElement; async presentActionSheet() { // 先关闭已存在的菜单 if (this.actionSheet) { await this.actionSheet.dismiss(); } this.actionSheet await this.actionSheetCtrl.create({ // 配置... }); await this.actionSheet.present(); } ngOnDestroy() { if (this.actionSheet) { this.actionSheet.dismiss(); } }5.2 无障碍访问优化确保上拉菜单符合无障碍标准const actionSheet await this.actionSheetCtrl.create({ header: 操作选项, buttons: [ { text: 删除, role: destructive, icon: trash, ariaLabel: 删除项目, handler: () {} }, // 其他按钮... ], backdropDismiss: true, // 允许点击背景关闭 keyboardClose: true // 键盘打开时自动关闭 });5.3 移动端性能考量在低端设备上优化性能的技巧避免在单个菜单中添加过多按钮建议不超过6个复杂图标使用SVG格式而非字体图标减少菜单打开时的同步操作async presentOptimizedActionSheet() { // 预加载可能需要的资源 await this.preloadResources(); const actionSheet await this.actionSheetCtrl.create({ // 精简配置... }); // 使用requestAnimationFrame确保流畅动画 requestAnimationFrame(async () { await actionSheet.present(); }); }6. 常见问题排查与解决方案6.1 菜单无法显示的典型原因未正确注入控制器确保在构造函数中注入ActionSheetController检查提供者是否在正确模块中声明异步方法未正确await// 错误示例 presentActionSheet() { this.actionSheetCtrl.create({...}).present(); } // 正确示例 async presentActionSheet() { const actionSheet await this.actionSheetCtrl.create({...}); await actionSheet.present(); }CSS冲突检查全局样式是否覆盖了Action Sheet的样式使用Chrome开发者工具检查元素层级6.2 按钮点击无响应的调试技巧检查handler函数绑定确保handler使用箭头函数或正确绑定this// 正确绑定示例 handler: () this.method(), // 或 handler: this.method.bind(this)验证事件传播添加console.log确认handler是否被调用检查是否有其他事件阻止冒泡测试role属性影响某些role如destructive可能有特殊行为尝试移除role属性进行隔离测试6.3 样式异常的解决方案平台样式不一致显式设置mode: ios或mode: md使用媒体查询针对不同平台调整样式自定义样式不生效确保CSS变量使用正确前缀检查样式作用域是否正确图标显示问题确认图标名称与Ionic图标集匹配检查是否导入了图标库7. 进阶应用复杂场景实现方案7.1 嵌套上拉菜单的实现对于复杂操作流可以实现菜单的层级结构async presentNestedActionSheet() { const primarySheet await this.actionSheetCtrl.create({ header: 主要操作, buttons: [ { text: 更多选项..., handler: async () { await primarySheet.dismiss(); this.presentSecondaryActionSheet(); return false; // 阻止自动关闭 } }, // 其他按钮... ] }); await primarySheet.present(); } async presentSecondaryActionSheet() { const secondarySheet await this.actionSheetCtrl.create({ header: 二级菜单, buttons: [ // 二级菜单项... ] }); await secondarySheet.present(); }7.2 与路由系统的集成在页面跳转场景下的优化处理async presentNavigationActionSheet() { const actionSheet await this.actionSheetCtrl.create({ buttons: [ { text: 跳转到设置, handler: async () { await actionSheet.dismiss(); this.router.navigate([/settings]); return false; } }, // 其他按钮... ] }); await actionSheet.present(); }7.3 结合状态管理的解决方案在大型应用中使用NgRx等状态管理库时的最佳实践async presentStateDrivenActionSheet() { const currentState this.store.select(currentSelection); const actionSheet await this.actionSheetCtrl.create({ buttons: [ { text: 添加到收藏, icon: heart, handler: () { this.store.dispatch(addToFavorites()); } }, // 其他状态相关操作... ] }); await actionSheet.present(); }8. 测试策略与质量保障8.1 单元测试实现方案使用Jasmine和Angular测试工具对上拉菜单进行测试describe(ActionSheet测试, () { let actionSheetCtrl: ActionSheetController; beforeEach(async () { await TestBed.configureTestingModule({ imports: [IonicModule.forRoot()] }).compileComponents(); actionSheetCtrl TestBed.inject(ActionSheetController); }); it(应该正确创建上拉菜单, async () { spyOn(actionSheetCtrl, create).and.callThrough(); await component.presentActionSheet(); expect(actionSheetCtrl.create).toHaveBeenCalled(); expect(actionSheetCtrl.create).toHaveBeenCalledWith(jasmine.objectContaining({ header: jasmine.any(String), buttons: jasmine.any(Array) })); }); it(点击删除按钮应触发删除逻辑, async () { spyOn(console, log); const actionSheet await actionSheetCtrl.create({ buttons: [{ text: 删除, handler: () console.log(删除操作触发) }] }); await actionSheet.present(); const button actionSheet.querySelector(.action-sheet-button); button.click(); expect(console.log).toHaveBeenCalledWith(删除操作触发); }); });8.2 E2E测试集成使用Cypress进行端到端测试的示例describe(上拉菜单E2E测试, () { it(应该显示和操作上拉菜单, () { cy.visit(/); cy.get(ion-button).contains(显示菜单).click(); cy.get(ion-action-sheet).should(be.visible); cy.contains(ion-action-sheet button, 删除).click(); cy.get(ion-action-sheet).should(not.be.visible); }); });8.3 视觉回归测试使用工具如Percy确保UI一致性describe(视觉测试, () { it(上拉菜单视觉一致性, () { cy.visit(/); cy.get(ion-button).click(); cy.percySnapshot(Action Sheet - 默认状态); }); });9. 实际项目中的经验总结在长期使用Ionic上拉菜单组件的过程中我积累了一些关键经验性能敏感场景在列表项中使用上拉菜单时避免为每个项都创建独立的handler函数这会导致大量函数实例。推荐使用参数化方法createItemActionHandler(itemId: string) { return () { this.handleItemAction(itemId); }; } async presentItemActionSheet(item: Item) { const actionSheet await this.actionSheetCtrl.create({ buttons: [ { text: 编辑, handler: this.createItemActionHandler(item.id) }, // 其他按钮... ] }); await actionSheet.present(); }国际化处理在多语言应用中动态加载翻译文本async presentLocalizedActionSheet() { const translations await this.translateService.get([ ACTIONS.DELETE, ACTIONS.SHARE, ACTIONS.CANCEL ]).toPromise(); const actionSheet await this.actionSheetCtrl.create({ buttons: [ { text: translations[ACTIONS.DELETE], role: destructive }, // 其他按钮... ] }); await actionSheet.present(); }主题适配技巧根据应用主题动态调整样式async presentThemedActionSheet() { const isDark await this.themeService.isDarkMode(); const actionSheet await this.actionSheetCtrl.create({ cssClass: isDark ? dark-action-sheet : , buttons: [...] }); await actionSheet.present(); }手势操作增强结合Ionic的手势系统创建更自然的交互import { Gesture, GestureController } from ionic/angular; constructor(private gestureCtrl: GestureController) {} setupLongPressAction(element: HTMLElement) { const gesture this.gestureCtrl.create({ el: element, gestureName: long-press, threshold: 500, onStart: () { this.pressActionSheet(); } }); gesture.enable(); }10. 扩展思路创新交互模式探索10.1 动态内容上拉菜单实现内容随手势拖动动态变化的效果async presentDynamicContentSheet() { const sheet await this.actionSheetCtrl.create({ header: 滑动调整, backdropDismiss: false, buttons: [{ text: 确认, handler: (data) { console.log(最终值:, data.value); } }] }); await sheet.present(); // 添加自定义内容 const content ion-range value50 pintrue (ionChange)updateValue($event) /ion-range ; const contentEl sheet.querySelector(.action-sheet-content); contentEl.innerHTML content; // 暴露方法给动态内容 sheet.updateValue (ev) { sheet.data { value: ev.detail.value }; }; }10.2 分步操作上拉菜单复杂操作分解为多个步骤async presentMultiStepActionSheet() { const step1 await this.actionSheetCtrl.create({ header: 步骤1/3: 选择类型, buttons: [ { text: 类型A, handler: () this.setType(A) }, { text: 下一步, handler: async () { await step1.dismiss(); this.presentStep2(); return false; } } ] }); await step1.present(); } async presentStep2() { // 第二步实现... }10.3 实时数据上拉菜单展示实时更新的数据async presentLiveDataActionSheet() { const sheet await this.actionSheetCtrl.create({ header: 实时数据, buttons: [{ text: 关闭, role: cancel }] }); await sheet.present(); // 添加实时数据视图 const content div classlive-data p当前值: span idcurrentValue0/span/p /div ; sheet.querySelector(.action-sheet-content).innerHTML content; // 模拟数据更新 const valueEl sheet.querySelector(#currentValue); let count 0; const interval setInterval(() { count; valueEl.textContent count.toString(); }, 1000); sheet.onDidDismiss().then(() { clearInterval(interval); }); }11. 与其他Ionic组件的协同使用11.1 结合Toast通知操作完成后提供视觉反馈async presentActionSheetWithFeedback() { const actionSheet await this.actionSheetCtrl.create({ buttons: [ { text: 完成项目, handler: async () { const toast await this.toastCtrl.create({ message: 项目已完成, duration: 2000 }); await toast.present(); return true; } } ] }); await actionSheet.present(); }11.2 与Loading指示器配合长时间操作时显示加载状态async presentActionSheetWithLoading() { const actionSheet await this.actionSheetCtrl.create({ buttons: [ { text: 同步数据, handler: async () { const loading await this.loadingCtrl.create(); await loading.present(); try { await this.syncData(); await loading.dismiss(); } catch (error) { await loading.dismiss(); this.showError(error); return false; // 保持菜单打开 } return true; // 关闭菜单 } } ] }); await actionSheet.present(); }11.3 在Modal中使用上拉菜单模态窗口中嵌套操作菜单async presentModalWithActions() { const modal await this.modalCtrl.create({ component: MyModalPage }); await modal.present(); // 在模态中打开菜单 modal.onDidDismiss().then(() { this.presentActionSheet(); }); }12. 设计系统集成方案12.1 创建可复用的Action Sheet服务将常用菜单模式封装为服务Injectable({ providedIn: root }) export class ActionSheetService { constructor(private actionSheetCtrl: ActionSheetController) {} async presentDeleteConfirmation(itemName: string): Promiseboolean { return new Promise(async (resolve) { const sheet await this.actionSheetCtrl.create({ header: 删除 ${itemName}?, buttons: [ { text: 确认删除, role: destructive, handler: () resolve(true) }, { text: 取消, role: cancel, handler: () resolve(false) } ] }); await sheet.present(); }); } // 其他常用菜单模式... }12.2 标准化按钮配置定义统一的按钮配置规范interface ActionSheetButtonConfig { text: string; icon?: string; role?: cancel | destructive; handler?: () any; cssClass?: string; } class ActionSheetBuilder { private buttons: ActionSheetButtonConfig[] []; addButton(config: ActionSheetButtonConfig): this { this.buttons.push(config); return this; } async present(header: string): Promisevoid { const sheet await this.actionSheetCtrl.create({ header, buttons: this.buttons }); await sheet.present(); } }12.3 主题化样式方案创建与设计系统一致的样式变量// 全局variables.scss $action-sheet-primary: var(--ion-color-primary); $action-sheet-danger: var(--ion-color-danger); $action-sheet-background: var(--ion-color-light); // 组件样式 ion-action-sheet { --button-color: #{$action-sheet-primary}; --background: #{$action-sheet-background}; .action-sheet-button.destructive { color: #{$action-sheet-danger}; } }13. 调试技巧与开发者工具13.1 Chrome开发者工具实战检查Action Sheet DOM结构打开开发者工具(Elements面板)触发上拉菜单后使用Select element工具选择菜单查看自动生成的DOM结构和类名动态修改样式选中菜单元素后在Styles面板中实时调整--background等CSS变量覆盖默认样式进行快速原型测试调试按钮点击事件在Sources面板中设置事件监听断点查找ion-action-sheet-button的click事件13.2 日志增强策略添加详细的调试日志async presentActionSheetWithLogging() { console.debug(开始创建Action Sheet); const startTime performance.now(); const actionSheet await this.actionSheetCtrl.create({ buttons: [ { text: 测试按钮, handler: () { console.log(按钮点击时间:, new Date().toISOString()); return true; } } ] }); actionSheet.onWillDismiss().then(() { const duration performance.now() - startTime; console.debug(Action Sheet显示时长: ${duration.toFixed(2)}ms); }); await actionSheet.present(); console.debug(Action Sheet已显示); }13.3 性能分析技巧使用Chrome Performance工具记录菜单操作打开Performance面板开始录制触发上拉菜单并完成一系列交互停止录制并分析菜单创建的耗时动画帧率内存变化情况14. 版本兼容性与升级策略14.1 Ionic 4/5/6的差异处理不同版本间的关键区别特性Ionic 4Ionic 5控制器注入方式需要手动提供自动提供CSS变量前缀无有(如--ion-)动画实现Web Animations APICSS动画兼容性处理方案private async presentCompatActionSheet() { // Ionic 6方式 if (this.actionSheetCtrl.create) { const sheet await this.actionSheetCtrl.create({...}); return sheet.present(); } // Ionic 4回退方案 return new Promise((resolve) { const sheet document.createElement(ion-action-sheet); // 手动设置属性... document.body.appendChild(sheet); sheet.present(); }); }14.2 Angular版本适配要点针对不同Angular版本的调整Angular 13使用独立组件APIimport { ActionSheet } from ionic/angular/standalone; Component({ standalone: true, imports: [ActionSheet] })Angular 12需将ActionSheetController添加到providers变更检测优化Component({ changeDetection: ChangeDetectionStrategy.OnPush }) export class MyComponent { constructor(private cdr: ChangeDetectorRef) {} async presentSheet() { const sheet await this.actionSheetCtrl.create({...}); await sheet.present(); this.cdr.markForCheck(); } }14.3 迁移指南从旧版Action Sheet升级从Ionic 3升级到最新版的步骤控制器注入变更// Ionic 3 constructor(public actionSheetCtrl: ActionSheetController) {} // Ionic 5 constructor(private actionSheetCtrl: ActionSheetController) {}配置对象差异// Ionic 3 buttons: [{ text: Ok, handler: function() {...} }] // Ionic 5 buttons: [{ text: Ok, handler: () {...} }]样式作用域变化Ionic 3: 样式封装在组件内Ionic 5: 使用Shadow DOM需要CSS变量覆盖15. 安全考量与用户隐私15.1 敏感操作确认机制对于关键操作实现二次确认async presentDeleteConfirmation() { const confirmSheet await this.actionSheetCtrl.create({ header: 确认删除?, subHeader: 此操作不可撤销, buttons: [ { text: 输入密码确认删除, handler: async () { const isValid await this.presentPasswordPrompt(); return isValid; // 只有返回true才会关闭菜单 } }, { text: 取消, role: cancel } ] }); await confirmSheet.present(); } async presentPasswordPrompt(): Promiseboolean { const prompt await this.actionSheetCtrl.create({ header: 输入管理员密码, inputs: [ { name: password, type: password, placeholder: 密码 } ], buttons: [ { text: 确认, handler: (data) { return this.authService.validatePassword(data.password); } } ] }); await prompt.present(); const result await prompt.onDidDismiss(); return result.data?.validated || false; }15.2 用户操作日志记录关键操作添加审计日志async presentAuditableActionSheet() { const sheet await this.actionSheetCtrl.create({ buttons: [ { text: 执行操作, handler: async () { await this.auditService.log(ActionSheet - 操作执行, { timestamp: new Date(), user: this.currentUser.id }); return true; } } ] }); await sheet.present(); }15.3 权限控制集成基于用户权限动态显示菜单项async presentRoleBasedActionSheet() { const userRoles await this.authService.getCurrentUserRoles(); const buttons []; if (userRoles.includes(editor)) { buttons.push({ text: 编辑内容, handler: () this.openEditor() }); } if (userRoles.includes(admin)) { buttons.push({ text: 管理设置, handler: () this.openAdminPanel() }); } buttons.push({ text: 取消, role: cancel }); const sheet await this.actionSheetCtrl.create({ header: 可用操作, buttons }); await sheet.present(); }16. 移动端专属优化技巧16.1 大屏设备适配方案针对平板和折叠屏设备的优化async presentAdaptiveActionSheet() { const isLargeScreen window.innerWidth 768; const sheet await this.actionSheetCtrl.create({ cssClass: isLargeScreen ? large-screen-sheet : , position: isLargeScreen ? middle : bottom, buttons: [...] }); await sheet.present(); }对应样式.large-screen-sheet { --width: 400px; --max-width: 80%; margin: auto; border-radius: 12px; }16.2 手势操作增强添加滑动手势支持async presentSwipeableActionSheet() { const sheet await this.actionSheetCtrl.create({ buttons: [...], backdropDismiss: false // 禁用背景点击关闭 }); await sheet.present(); // 添加滑动手势 const gesture this.gestureCtrl.create({ el: sheet, gestureName: swipe-down, direction: y, threshold: 30, onMove: (detail) { if (detail.deltaY 0) { sheet.style.setProperty(transform, translateY(${detail.deltaY}px)); } }, onEnd: (detail) { if (detail.deltaY 100) { sheet.dismiss(); } else { sheet.style.setProperty(transform, translateY(0)); } } }); gesture.enable(); }16.3 键盘弹出处理输入型Action Sheet的键盘管理async presentInputActionSheet() { const sheet await this.actionSheetCtrl.create({ inputs: [ { name: comment, type: text, placeholder: 输入评论 } ], buttons: [...] }); await sheet.present(); // 自动聚焦输入框 setTimeout(() { const input sheet.querySelector(input); input?.focus(); }, 300); // 键盘弹出时调整位置 window.addEventListener(keyboardWillShow, () { sheet.style.setProperty(transform, translateY(-100px)); }); window.addEventListener(keyboardWillHide, () { sheet.style.setProperty(transform, translateY(0)); }); }17. 与其他框架的集成方案17.1 在React Ionic中使用React版本的实现方式import { IonActionSheet } from ionic/react; const MyComponent: React.FC () { const [showActionSheet, setShowActionSheet] useState(false); return ( IonButton onClick{() setShowActionSheet(true)} 显示菜单 /IonButton IonActionSheet isOpen{showActionSheet} onDidDismiss{() setShowActionSheet(false)} header操作选项 buttons{[ { text: 删除, role: destructive, handler: () console.log(删除) }, { text: 取消, role: cancel } ]} / / ); };17.2 Vue Ionic集成方案Vue 3的组合式API实现script setup import { IonActionSheet, IonButton } from ionic/vue; import { ref } from vue; const isOpen ref(false); const presentActionSheet () isOpen.value true; /script template ion-button clickpresentActionSheet 显示菜单 /ion-button ion-action-sheet :is-openisOpen header操作选项 :buttons[ { text: 分享, handler: () console.log(分享) }, { text: 取消, role: cancel } ] didDismissisOpen false / /template17.3 原生JavaScript项目集成纯HTML/JS项目中使用ion-app ion-button idactionButton显示菜单/ion-button /ion-app script typemodule import { actionSheetController } from https://cdn.jsdelivr.net/npm/ionic/core/dist/ionic/ionic.esm.js; document.getElementById(actionButton).addEventListener(click, async () { const sheet await actionSheetController.create({ header: 操作, buttons: [ { text: 确定, handler: () console.log(确认) } ] }); await sheet.present(); }); /script18. 设计模式与架构思考18.1 命令模式实现将菜单操作抽象为命令对象interface ActionSheetCommand { execute(): Promisevoid; text: string; icon?: string; } class DeleteCommand implements ActionSheetCommand { constructor(private item: Item) {} text 删除; icon trash; async execute() { await this.item.delete(); } } async presentCommandActionSheet(commands: ActionSheetCommand[]) { const sheet await this.actionSheetCtrl.create({ buttons: commands.map(cmd ({ text: cmd.text, icon: cmd.icon, handler: () cmd.execute() })) }); await sheet.present(); }18.2 状态管理模式结合状态机管理菜单流程class ActionSheetState { private currentState: idle | showing | processing idle; async present() { if (this.currentState ! idle) return; this.currentState showing; const sheet await this.actionSheetCtrl.create({...}); sheet.onWillDismiss().then(() { this.currentState idle; }); await sheet.present(); } async handleAction(action: () Promisevoid) { if (this.currentState ! showing) return; this.currentState processing; try { await action(); } finally { this.currentState idle; } } }18.3 响应式编程实现使用RxJS管理菜单流class ActionSheetStream { private action$ new SubjectActionSheetButton(); constructor(private actionSheetCtrl: ActionSheetController) {} async present(options: ActionSheetOptions) { const sheet await this.actionSheetCtrl.create(options); sheet.buttons.forEach(button { if (button.handler) { const originalHandler button.handler; button.handler () { const result originalHandler();
返回列表