背景
不久以前有一篇文章介绍过关于AngularJS路由模块参数resolve的分析,当时的分析过程是基于代码运行表象推断的,并没有具体深入源码进行探究。今天看到AngularJs 弹窗模块 $uibModal 的API中也用了resolve这个属性,跟踪源码发现其使用该参数的流程跟前面分析的结果基本一致。
本文就来看看$uibModal的open方法的源码吧。
$uibModal基本用法
该模块用于弹出一个模块对话框,基本用法demo:
var modalInstance = $uibModal.open({
animation: false,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: tplUrl,//弹框页面路径
controller: ctrler,//引用的Controller
controllerAs: 'ctrl',
windowClass: 'fullModal',
size: size,
appendTo: parentElem,//弹框父元素
resolve: {
data: function () {
return data;
},
title: function(){
return title;
},
load: ['$q', '$rootScope', function($q, $rootScope) {
var defer = $q.defer();
require([ctrlerUrl], function() {
$rootScope.$apply(function() {
defer.resolve();
});
});
return defer.promise;
}]
}
});
这里创建了一个模态对话框的实例对象modalInstance ,它需要一个resolve的属性,该属性提供了三个工厂方法,此外还引用了一个Controller和模板URL。
源码分析
点击open后,选择其实现文件ui-bootstrap-tpls.js,随便选择一个版本:
进入源码定义部分,在当前源码页面搜索resolve字符串信息,找到几段关键的源码。
首先,我们关注模块属性设置的源码:
$modal.open = function (modalOptions) {
var modalResultDeferred = $q.defer();
var modalOpenedDeferred = $q.defer();
//prepare an instance of a modal to be injected into controllers and returned to a caller
var modalInstance = {
result: modalResultDeferred.promise,
opened: modalOpenedDeferred.promise,
close: function (result) {
$modalStack.close(modalInstance, result);
},
dismiss: function (reason) {
$modalStack.dismiss(modalInstance, reason);
}
};
//merge and clean up options
modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
modalOptions.resolve = modalOptions.resolve || {};
此处将open的入参的resolve配置收集到成员变量resolve中。
其次,定位到下一处使用resolve的地方:
var templateAndResolvePromise =
$q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
……
这里使用了$q.all,等待resolve中所有使用了defer的地方都执行完成。
第三,我们关注resolve与controller之间的关联逻辑:
//controllers
if (modalOptions.controller) {
ctrlLocals.$scope = modalScope;
ctrlLocals.$modalInstance = modalInstance;
//遍历每个resolve的工厂方法
angular.forEach(modalOptions.resolve, function (value, key) {
ctrlLocals[key] = tplAndVars[resolveIter++];
});
//创建控制器的实例对象
ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
}
在$q.all操作之后,才对Controller进行处理,处理操作包括两个步骤:
1、遍历resolve的工厂方法并将执行结果收集到ctrlLocals这样一个map对象中。
2、以ctrlInstance 作为一部分参数来创建Controller实例ctrlInstance 。
3、打开对话框页面:
$modalStack.open(modalInstance, {
scope: modalScope,
deferred: modalResultDeferred,
content: tplAndVars[0],
backdrop: modalOptions.backdrop,
keyboard: modalOptions.keyboard,
windowClass: modalOptions.windowClass,
windowTemplateUrl: modalOptions.windowTemplateUrl,
size: modalOptions.size
});
启示录
模态对话框中resolve的作用是依赖加载和数据预准备,流程为:
- 执行依赖代码
- 进行数据准备
- 实例化Controller
- 打开模板文件
推而广之,大概所有使用resolve属性的模块,其流程应该都跟此类似吧。