AngularJS模块中resolve参数分析

时间:2024-04-03 13:32:04

背景

不久以前有一篇文章介绍过关于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,随便选择一个版本:
AngularJS模块中resolve参数分析

进入源码定义部分,在当前源码页面搜索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的作用是依赖加载和数据预准备,流程为:

  1. 执行依赖代码
  2. 进行数据准备
  3. 实例化Controller
  4. 打开模板文件

推而广之,大概所有使用resolve属性的模块,其流程应该都跟此类似吧。