angular-modal-service源码深潜:showModal从获取模板到$compile编译的完整流程拆解

📅 2026/8/25 8:52:12
angular-modal-service源码深潜:showModal从获取模板到$compile编译的完整流程拆解
angular-modal-service源码深潜showModal从获取模板到$compile编译的完整流程拆解【免费下载链接】angular-modal-serviceModal service for AngularJS - supports creating popups and modals via a service.项目地址: https://gitcode.com/gh_mirrors/an/angular-modal-serviceangular-modal-service 是 AngularJS 生态中一个轻量级的弹窗Modal服务核心 API 只有一个 ——showModal。一次调用就能完成获取模板 → 创建作用域 → $compile 编译 → 注入控制器 → 插入 DOM。今天我们把整个流程完整拆解一遍新手也能看懂。先看整体showModal 的 7 步流程整个服务实现集中在单文件 src/angular-modal-service.js约 320 行showModal定义在 src/angular-modal-service.js。调用一次ModalService.showModal(options)后源码按以下顺序执行步骤做什么关键代码位置1️⃣ 参数校验检查controller缺失则 rejectL130-L1342️⃣ 获取模板getTemplate取 template/templateUrlL29-L443️⃣ 创建作用域modalScopeclose函数注入L144-L1974️⃣ 编译模板$compile link 生成 DOML204-L2065️⃣ 创建控制器$controller注入 inputs 和 closeL2106️⃣ 插入 DOM$animate.enter添加到 body/自定义容器L217-L2287️⃣ 返回 modal 对象携带 element/scope/close 等属性L236-L251调用侧的完整长这样摘自 samples/sampleapp.jsModalService.showModal({ templateUrl: yesno/yesno.html, controller: YesNoController }).then(function(modal) { modal.element.modal(); modal.close.then(function(result) { /* 拿到弹窗结果 */ }); });第 1 步参数校验与组件模式showModal入口先做一件防御性的事检查是否传了controller没有则直接 reject 并返回src/angular-modal-service.js。这是整个 Promise 链最早的失败点。如果传的是componentAngularJS 1.5 组件源码会先调用setupComponentOptionsL113-L116自动把组件名转成 kebab-case 模板并生成一个内联控制器把bindings透传给组件 —— 也就是说组件模式最终仍然走模板 控制器这条主线。第 2 步getTemplate —— 模板从哪里来模板获取封装在getTemplate函数src/angular-modal-service.js优先级如下template直接给了 HTML 字符串 → 立即 resolve不发请求templateUrl走 AngularJS 的$templateRequest(templateUrl, true)发 HTTP 请求第二个参数true表示启用$templateCache缓存——同一个弹窗第二次打开就不会再请求服务器两者都没有reject 出No template or templateUrl has been specified.。模板缓存行为有专门的测试覆盖见 test/template.spec.js。请求失败时错误会沿 Promise 链传播到最外层L308-L310这就是为什么调用方可以统一用.catch兜底。第 3 步创建独立 scope 与 close 机制拿到模板后getTemplate(...).then(function (template) {...})源码开始组装弹窗的三件套scope(options.scope || $rootScope).$new()L144——弹窗默认挂在$rootScope下拥有独立子作用域也可以通过scope选项指定父级避免路由切换时误关弹窗inputs 对象包含$scope和一个核心的close(result, delay)函数L177-L197。close是弹窗内部自杀按钮调用后先触发可选的preClose钩子再通过$timeout(delay)延迟销毁给关闭动画留时间防重入hasAlreadyBeenClosed标志位保证close只生效一次。此外还会注册$locationChangeSuccess监听L148-L164路由一变就自动关闭弹窗可通过locationChangeSuccess选项设为false禁用或传毫秒数延迟相关行为可在 test/dom.spec.js 中对照阅读。第 4 步$compile 编译 —— 本文标题的关键这是整个流程的技术核心只有两行src/angular-modal-service.jsvar linkFn $compile(template); var modalElement linkFn(modalScope); inputs.$element modalElement;$compile(template)把 HTML 字符串变成链接函数linkFnlinkFn(modalScope)执行链接生成真实 DOM 元素modalElement并把模板里的ng-click、{{ }}等指令与modalScope绑定生成后立刻把元素挂到inputs.$element这样弹窗控制器里可以注入$element直接操作 DOM例如手动调用 Bootstrap 的modal(hide)。模板只有一个根元素这一点很重要链接函数返回的元素集合会被整体插入 DOM 并交给modal.element多个顶层元素哪怕多一条 HTML 注释会导致行为异常README 的 FAQ 专门讨论了这个坑。第 5 步$controller 创建控制器var modalController $controller(options.controller, inputs, false, options.controllerAs);src/angular-modal-service.js注意三个细节第二个参数是上一步组装的inputs已用angular.extend合并了用户传入的options.inputs见 L200所以close、$element和自定义数据都能按名称注入控制器构造函数第三个参数false表示不自动把控制器实例挂到 scope 上改由controllerAs手动控制支持内联控制器函数 controllerAs的写法源码会用angular.extend合并同名实例L212-L214。控制器侧只需声明注入close如 samples/yesno/yesnocontroller.jsclose(result, 500)即携带结果关闭预留 500ms 动画时间。第 6 步$animate.enter 插入 DOM插入前会先确定目标容器L216-L228默认挂到bodyappendElement为字符串时按 CSS 选择器查询 DOMappendElement为元素对象时直接挂上去。真正的插入由appendChild辅助函数完成L49-L55它调用$animate.enter而不是append因此进入时会自动触发ng-enter动画钩子 —— 配合 ngAnimate 就能零 JS 实现淡入。如果设置了bodyClass还会在 body 上加一个临时 classL231-L233方便用 CSS 控制背景滚动锁定等效果。第 7 步返回 modal 对象Promise resolve最后源码填充modal对象并deferred.resolve(modal)L236-L251调用方在.then中拿到的就是它属性说明modal.element编译好的 DOM 元素可modal.element.modal()调用 Bootstrapmodal.scope/modal.controller弹窗的 scope 和控制器实例modal.closeclose函数被调用时立即 resolve 的 Promisemodal.closed关闭动画播完、元素彻底移除后才 resolve 的 Promiseresolve 之前还有一个细节若控制器定义了$onInit1.6 组件生命周期会主动调用L246-L248随后document.activeElement.blur()清除输入焦点防止连续按回车重复弹出多个弹窗。每个打开的弹窗都会 push 进self.openModals数组L257供closeModals(result, delay)L58-L64一键全关。关闭与清理防内存泄漏的最后一步close触发的cleanUpClosesrc/angular-modal-service.js值得单独看它是优雅销毁的范本resolveclosePromise移除bodyClass$animate.leave(modalElement)触发ng-leave退出动画动画结束后resolveclosed→modalScope.$destroy()→ 从openModals移除把inputs、deferred、modal等闭包引用全部置null——源码注释直言这是为了防止内存泄漏。close点了关闭与closed彻底移除两个 Promise 的分工就是回调时机与清理时机的分离值得在自己的服务设计里借鉴。上手练习跑示例、读测试想亲手验证以上每一步建议按这个顺序获取源码git clone https://gitcode.com/gh_mirrors/an/angular-modal-servicenpm install npm test跑测试重点看 test/template.spec.js模板获取与缓存、test/dom.spec.jsDOM 插入与移除、test/close.spec.jsclose 行为npm start后打开 localhost:8080 玩 samples/ 下的三个示例yesno基础、complexinputs preClose、custombodyClass对照本文的 7 步在showModal里下断点单步走一遍。全局默认值如closeDelay通过 Provider 配置见 ModalServiceProvider.configureOptions 及示例 samples/sampleapp.js。小结angular-modal-service 用不到 320 行代码给出了一个教科书级的弹窗服务实现$q 驱动的全流程 Promise 化、$compile 编译复用、inputs 注入解耦、$animate 动画钩子、显式置 null 防泄漏。读懂showModal这一个函数基本就掌握了 AngularJS 1.x 里 service compile controller 协作的完整套路。✅【免费下载链接】angular-modal-serviceModal service for AngularJS - supports creating popups and modals via a service.项目地址: https://gitcode.com/gh_mirrors/an/angular-modal-service创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考