在ngApp之外应用动画

时间:2014-03-21 18:02:17

标签: angularjs angularjs-directive

我的目标是创建一个可以应用于给定元素的指令,这样当单击该元素时,就会创建一个模态。我想创建模态并附加到body节点,该节点位于我的ng-app元素之外。由于页面上有多个应用程序的要求,我无法在<html><body>标记上添加ng-app。然而,对于正确的z定位,我会尽可能地将模态元素置于体内。

我的指示如下:

var module = angular.module('Test', ['ngAnimate']);
module.directive('modal', function($compile, $animate) {
    function link(scope, element, attr) {
        element.on('click', function () {
            var modal = $compile('<div class="modal"></div>')(scope);
            scope.$apply(function () {
                $animate.enter(modal, angular.element(document.body));
            });
        });

    }

    return {
        link: link,
        scope: {}
    };

});

当我使用$animate.entermodal附加到正文时,会附加它,但动画不会运行。我的HTML看起来像这样:

  <body>
    <div ng-app="Test">
      <button modal>Open Modal</button>
    </div>
  </body>

如果我将ng-appdiv移动到body,则动画可以正常运行。但是我无法做到这一点,因为我需要选择在给定页面上放置多个ng-app

有可能吗?

工作(或不工作)的例子:

http://plnkr.co/edit/vUi2PmLjea36nrJ9i3R2?p=preview

1 个答案:

答案 0 :(得分:2)

简短的回答是:不,你不能 (至少Angular似乎不是为了允许它而设计的)。


更长的答案是:不,你不能。原因如下:

原因是$animate目前的实施方式:

在你的情况下,它将elements =添加到DOM(特别是document.body),然后检查是否应继续使用动画特定的东西(或者动画是否为#34;禁用&# 34)
。 根据源代码,检查动画是否被禁用的功能&#34;是:

function animationsDisabled(element, parentElement) {
  if (rootAnimateState.disabled) return true;

  if(isMatchingElement(element, $rootElement)) {
    return rootAnimateState.disabled || rootAnimateState.running;
  }

  do {
    //the element did not reach the root element which means that it
    //is not apart of the DOM. Therefore there is no reason to do
    //any animations on it
    if(parentElement.length === 0) break;

    var isRoot = isMatchingElement(parentElement, $rootElement);
    var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
    var result = state && (!!state.disabled || state.running || state.totalActive > 0);
    if(isRoot || result) {
      return result;
    }

    if(isRoot) return true;
  }
  while(parentElement = parentElement.parent());

  return true;
}

正如您所看到的,由于您的模态不是$ rootElement的子项(但兄弟),isRoot将始终为false,do-while循环将一直运行,直到没有父项 - 元件。此时if(parentElement.length === 0) break;将中断循环,函数将返回true,从而取消动画。


最长的答案是:取决于(你需要它多么糟糕)。

可用选项(我能想到)是:

  1. 接受你的命运并找到其他方式(除了$animate)来执行你的动画。

  2. 创建自己的angular-animate分支并更改一行代码,这样即使target-element不是$ rootElement的子代,动画也不会被取消。

  3. 如果您喜欢危险地生活:如果我们暂时换掉$ rootElement&#34;该怎么办? ?
    我尝试了各种方法(在本答案的范围之外)和我唯一可以工作的方法(?),交换了与jqLit​​e对象相关联的HTML元素(即$rootElement[0])。

  4. 我将功能包装在服务中(以及一些便利功能,例如在一段时间后恢复原始元素):

    module.factory('myRootElement', function ($rootElement, $timeout) {
        /* Save the original element for reference */
        var original = $rootElement[0];
    
        /* Fake the $rootElement for the specified 
         * period of time (in milliseconds) */
        function fakeForMillis(millis) {
            $rootElement[0] = document.body;
            $timeout(function () {
                $rootElement[0] = original;
            }, millis || 0);
        }
    
        /* Return the Service object */
        return {
            fakeForMillis: fakeForMillis
        };
    });
    

    最后,您只需要临时交换动画的$rootElement即可。 (不幸的是,你必须指定动画所需的时间,但我敢打赌,有更好的方法可以通过编程方式找到它 - 再次超出了这个答案的范围。)

    myRootElement.fakeForMillis(1000);
    $animate.enter(modal, angular.element(document.body));
    

    另请参阅此 short demo

    <子> 我不知道我在说什么,我决不会调查这种方法的后果,所以请自行承担风险,如果出现奇怪的事情,请不要感到惊讶。