【问题标题】:Angular directive's link function won't manipulate DOMAngular 指令的链接功能不会操纵 DOM
【发布时间】:2016-08-09 12:49:18
【问题描述】:

我正在尝试从服务访问数据,以便可以使用 jQuery 操作 ngRepeated 列表。我无法让link 函数工作。根据我所阅读的内容,我希望在任一

app.directive('myDir', function() {
  return {
    restrict: 'E',
    templateUrl: 'url.html',
    link: function(scope, el, attrs) {
      //jQuery to manipulate DOM here
    }
  };
});

app.directive('myDir', function() {
  return {
    restrict: 'E',
    templateUrl: 'url.html',
    compile: function(scope, el, attrs) {
      return {
        post: function() {
          //jQuery to manipulate DOM here
        }
      }
    }
  }
});

但是元素还没有在页面上,所以它不起作用。我错过了什么?

【问题讨论】:

  • 这是准确的代码吗?因为你已经合并了返回的对象(指令定义对象)和函数括号。
  • @GregL 不,这很快就被伪了,并且有大量的语法错误。对于那个很抱歉。已更新。
  • 你能在链接函数中放一个断点并确认调试器在那里中断吗?另外,你能显示从服务返回的 ng-repeat 和集合变量吗?

标签: javascript jquery angularjs model-view-controller


【解决方案1】:

尝试等待一个 $digest 阶段。这将为ng-repeat 提供足够的时间来呈现DOM 元素。您可以为此使用 $timeout。

link: function(scope, el, attrs) {
    $timeout(function() {
        //jQuery to manipulate DOM here
    });
}

编辑:

检查下面的示例。这里我们有my-dir 收集由ng-repeat 创建的所有元素。有一个帮助器 mockup-box 内部指令,其中包括 my-dir 父指令控制器并使用其方法注册自身(其元素)。

然后my-dir 控制器调用invalidateChildren 函数,该函数通过延迟validateChildren 函数执行来等待children 指令累积。

validateChildren 函数中,我们操作DOM 元素,我们确信它们会存在,因为每个元素仅在编译时才注册。 validateChildren 函数中有一些虚拟代码,但您可以在其中放置任何内容。

还有另一个额外的好处。我们不再依赖 ng-repeat,my-dir 也不知道如何创建子元素。他们甚至可能是“静态”的孩子。它还支持添加额外的孩子。如果 ng-repeat 生成其他元素,它们将再次在父控制器中注册并调用 validateChildren 函数。

angular.module('example', [])

.controller('AppCtrl', function($scope) {
  $scope.mockupData = ['a', 'b', 'c', 'd']
})

.directive('myDir', function() {
  return {
    controller: function($element, $timeout) {
      var ctrl = this;
      var childrenDirty = false;
      
      this.children = [];
      
      this.addChild = function(jqChild) {
        ctrl.children.push(jqChild);
        ctrl.invalidateChildren();
      };
      
      this.removeChild = function(jqChild) {
        var index = ctrl.children.indexOf(jqChild);

        if (index >= 0) {
          ctrl.children.splice(index, 1);
        }
      };
      
      this.invalidateChildren = function() {
        if (!childrenDirty) {
          childrenDirty = true;
          $timeout(ctrl.validateChildren);
        }
      };
      
      this.validateChildren = function() {
        childrenDirty = false;

        // Will be fire only once
        angular.forEach(ctrl.children, function(jqChild) {
          jqChild.html('value: "' + jqChild.html() + '"');
        }); 
      };
    },
    link: function() {
    }
  }
})

.directive('mockupBox', function() {
  return {
    require: '^myDir',
    link: function(scope, iElement, iAttrs, myDirCtrl) {
      myDirCtrl.addChild(iElement);
      
      scope.$on('$destroy', function() {
        myDirCtrl.removeChild(iElement);
      });
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="example" ng-controller="AppCtrl">
  <div my-dir>
    <div ng-repeat="data in mockupData" mockup-box>{{data}}</div>
  </div>
</div>

预期行为

  • mockupData 值为 ['a', 'b', 'c', 'd'];
  • validateChildren函数编辑子元素内容并在前面添加“value:”。

每个子元素的代码执行结果将是value: "&lt;letter&gt;"

【讨论】:

  • $timeout 会等待摘要循环再运行,即使没有给出时间是一个时间吗?
  • 是的。这就是想法。它将等待当前的 $digest 阶段完成,然后运行一个新的阶段。您传递的函数将在第二个 $digest 阶段执行。
  • 是的。您可以在 ng-repeat 集合上添加一个额外的 $watch,然后在 $watch 主体中执行 $timeout,或者您可以尝试在 $timeout 中与 $timeout 一起使用。
  • 这两种方法都会很麻烦,可能会停止工作。如果您可以在 ng-repeat 创建的每个元素上附加一个指令来做到这一点,那将是一个更干净的“Angular 友好”解决方案。
  • scope.$evalAsync(fn) 实际上似乎工作得很好。你以前用过吗?您认为这可能适合这种情况吗?
猜你喜欢
  • 2015-01-07
  • 1970-01-01
  • 1970-01-01
  • 2016-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-15
  • 1970-01-01
相关资源
最近更新 更多