【问题标题】:AngularJS watch DOM changeAngularJS 观察 DOM 变化
【发布时间】:2014-02-15 10:43:50
【问题描述】:

我有一个 auto-carousel 指令,它遍历链接元素的 children

然而,孩子们还没有加载到 DOM 中,因为他们的 ng-ifs 表达式还没有被解析。

如何确保父指令知道它的 DOM 树发生了变化?

        <ul class="unstyled" auto-carousel>
          <li class="slide" ng-if="name">{{name}}</li>
          ...
          <li class="slide" ng-if="email">{{email}}</li>
        </ul>

我可以使用$timeout,但这感觉不可靠。我也可以使用ng-show 而不是ng-if,但这不能回答问题,也不是我需要的。

【问题讨论】:

  • 根据 angularJS 文档,子指令优先于父指令。所以 ng-if 在父指令之前链接。所以解析顺序不是你的问题。也许您的数据是异步可用的,因此 ng-if 不会在链接时解析。
  • @Alborz 这很有趣。你有链接吗?如果是这种情况,我感觉指令在填充子元素的模型之前已链接,我将执行测试并回复您。不要忘记我的链接!
  • @Alborz 链接功能肯定会在模型填充后发生。我什至用了一个额外的$scope.$apply()
  • 我创建了一个示例。您将看到子链接在父链接之前执行。jsbin.com/udaTEraM/1/edit
  • @Alborz 感谢您花时间进行设置。但是我在ng-if$watch 处理程序中添加了一个console.log - 它显然是在父链接器之后执行的。有什么办法可以在孩子改变后调用父链接器?比如,注意 DOM 变化? (观察模型变化是个问题,因为这个指令是通用的并且到处都在使用)

标签: angularjs dom angularjs-directive angular-ng-if


【解决方案1】:

所以这就是我最终要做的:

我发现你可以将函数传递给$scope.$watch。从那里,返回要监视更改的表达式的值非常简单。它的工作方式与为作用域上的属性传递键字符串完全一样。

link: function ($scope, $el, $attrs) {
  $scope.$watch(
    function () { return $el[0].childNodes.length; },
    function (newValue, oldValue) {
      if (newValue !== oldValue) {
        // code goes here
      }
    }
  );
}

我在看childNodes,而不是children,因为childNodes 列表包含元素以及 文本节点和cmets。这是无价的,因为 Angular 使用注释占位符来表示 ng-repeatng-ifng-switchng-include 等指令,它们执行嵌入并更改 DOM,而 children 仅包含元素。

【讨论】:

  • 这很好!我有一种情况,每次在父级中添加或删除input 元素时,我都需要运行一个函数。所以,$scope.$watch(function () { return $(".parent input").length; }, ...。这非常有效,但我想知道性能..
  • 那么您将在每个$digest 循环中调用至少一个jQuery 查找(在这种情况下,也是一个非常糟糕的查找)。就表演而言,这是相当糟糕的。您可以缓存节点,然后使用 jQuery 的 find() 来改进
  • 我确实喜欢这样来监听 contenteditable 上的内容长度,如下所示:scope.$watch(function () { return element[0].innerHTML.length;}, function (n, o) { /*做一个操作*/ });
  • @SimonPertersen 这对contenteditables 来说是个聪明人!
  • 还建议使用angular.equals(newValue, oldValue)newValueoldValue 进行比较,在此答案的用例中,即使是可比较的值也是普通数字。
【解决方案2】:

如果您需要观察元素 dom 中更深层次的任何变化,MutationObserver 是您的最佳选择:

.directive('myDirective', function() {
    return {
        ...
        link: function(scope, element, attrs) {
            var observer = new MutationObserver(function(mutations) {
                // your code here ...
            });
            observer.observe(element[0], {
                childList: true,
                subtree: true
            });
        }
    };
});

【讨论】:

  • 这很有趣,但需要 IE 11。
【解决方案3】:

我为此angular-dom-events创建了一个指令模块

在你的情况下,你可以

    <ul class="unstyled" auto-carousel>
      <li class="slide" ng-if="name" dom-on-create="nameCreated()">{{name}}</li>
      <li class="slide" ng-if="email" dom-on-destroy="emailDestroyed()">{{email}}</li>
    </ul>

目前仅支持dom-on-createdom-on-destroy,但性能优于公认的答案,因为它只会为每个 dom 事件触发一次,而不是重复检查 $watch 回调。

【讨论】:

  • 正是我想要的。谢谢!
  • 如果您可以修改您的孩子,而不是在等待第三方元素出现时,这是一个不错的选择
【解决方案4】:

虽然我不认为它与 angular 的建议相符,但您可以使用 ng-init 来触发元素的初始化:

<ul class="unstyled" auto-carousel>
    <li class="slide" ng-if="name" ng-init="recheck()">{{name}}</li>
    <li class="slide" ng-if="email" ng-init="recheck()">{{email}}</li>
</ul>

【讨论】:

  • 典型 .. 正是我要找的东西,文档告诉我不要使用它 .. docs.angularjs.org/api/ng/directive/ngInit
  • 这是唯一对我有用的东西。在表单被动态插入到 dom 之后,我需要运行一些代码。
【解决方案5】:

您可以尝试先在链接函数中编译指令内容。例如:

angular.module('myApp').directive('autoCarousel', ['$compile', function ($compile) {

    return {
        templateUrl: 'views/auto-carousel.html',
        restrict: 'A',
        replace: true,
        link: function (scope, element, attr) {
            $compile(element.contents())(scope);

            // your code goes here
        }
    }
}]);

【讨论】:

    猜你喜欢
    • 2013-10-18
    • 1970-01-01
    • 1970-01-01
    • 2018-04-10
    • 2012-07-14
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 1970-01-01
    相关资源
    最近更新 更多