22

我有一个简单的指令,其中模板在其中使用 ng-repeat。我需要运行一些代码来针对 ng-repeat 指令创建的一些元素实例化一个 jquery 组件。问题是如果我把这段代码放在链接函数中。ng-repeat 还没有构建这些元素,所以没有实例化。

App.directive('myDirective', ['$compile', '$timeout', function($compile, $timeout) {
  return {
    scope: {
        domains: '='
    },
    templateUrl: '/app/partials/my_directive.html',
    link: function($scope, element, attributes) {
        element.find('.standard-help').tooltip('destroy');
        element.find('.standard-help').tooltip({placement: 'top', trigger: 'click hover focus'});
    }
  };
}

模板如下所示。我正在尝试附加

<ul class="media-list domainList">
  <li class="media" style="position:relative;" ng-repeat="domain in domains">
    <a class="domainHeader" href="javascript://">
        <span class="domainHeader">{{domain.tag}}</span>
    </a>
    <div class="media-body" style="margin-left: 52px;">
        <ul class="standardsList">
            <li ng-class="{ standardDisplayed: lessonLayout == 'standards' }" ng-hide="standard.lessons.length == 0" ng-repeat="standard in domain.standards">
                <a href="javascript://" title="{{standard.description}}" ng-show="lessonLayout == 'standards'" class="standard-help pull-right"><i class="icon-question-sign"></i></a>
                <h6 ng-show="lessonLayout == 'standards'">{{standard.tag}}</h6>
                <ul class="lessonsList">
                    <li ng-class="{ lesson: true }" ng-repeat="lesson in standard.lessons" ng-click="onLessonSelected(lesson)">
                        <i class="icon-lock lesson-locked"></i>
                        <div>{{lesson.title}}</div>
                    </li>
                </ul>
            </li>
        </ul>
    </div>
  </li>
</ul>

我尝试使用 $watch() 和 $observe() 在域更改时注册回调,然后实例化工具提示代码。但是,我似乎无法让它在正确的时间给我打电话。有什么我想念的想法吗?

4

5 回答 5

23

我发现如果创建了另一个指令,我将它添加到正在创建 ng-repeat 的元素中,它将为重复创建的每个元素通知它。然后我可以简单地 $emit 一个父指令可以监听的事件。此时它可以执行到那里重复元素的链接。这非常有效,特别是对于 dom 中的多个 ng-repeats,因为我可以通过它们的事件类型将它们分开,这些事件类型将传递给新指令。

这是我的指令:

App.directive("onRepeatDone", function() {
    return {
        restrict: 'A',
        link: function($scope, element, attributes ) {
            $scope.$emit(attributes["onRepeatDone"] || "repeat_done", element);
        }
    }
});

这是模板中该新指令的用法:

<ul>
    <li on-repeat-done="domain_done" ng-repeat="domain in domains">...</li>
</ul>

然后在父指令中,我可以执行以下操作:

link: function( $scope, element, attributes ) {
    $scope.$on('domain_done', function( domainElement ) {
        domainElement.find('.someElementInsideARepeatedElement').click(...);
    } );
}
于 2013-07-16T21:36:08.053 回答
10

$watch应该能够实现您想要做的事情:

link: function(scope, elem, attrs) {
    var init = function() {
        // your initialization code
    };
    var ulElem = elem.children()[0]; // ul element

    var unreg = scope.$watch(function() {
        return ulElem.children.length === scope.domains.length; // check if all "li" are generated
    }, function() {
        // at this point, the ul is rendered
        init();
        unreg(); // unregister the watcher for performance, since the init function only need to be called once
    });
}

我创建了一个plunk来演示这个解决方案。

于 2013-07-15T05:55:45.943 回答
2

我刚刚遇到了这个确切的问题,发现了我认为更好的解决方案。基本上,您可以在s 完成渲染$timeout后将任务排队。ng-repeat

注意:这并不意味着您实际上正在使用计时器或任何形式的时间延迟;这只是将函数附加到$digest循环末尾的一种简单方法。

下面是我的代码的摘录。该指令用于ng-repeat显示选项卡,因此,我必须等到它被渲染后才能运行我的tabSelected()方法,该方法将active类添加到 HTML 元素中。

link($scope:ng.IScope, element:JQuery, attributes:ng.IAttributes):void {
  if (this.autoActivateFirstTab && this.tabs && this.tabs.length > 0) {
    var self = this;

    var selectFirstTab = function() {
      self.tabSelected(self.tabs[0]);
    };

    // Add a $timeout task to the end of the $digest loop to select the
    // first tab after the loop finishes
    self.$timeout(selectFirstTab, 0);
  }
}

我从http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering找到了解决方案,该解决方案有一个现场演示,展示了它的实际效果。

告别词:如果您正在使用 Karma 测试您的代码(就像您应该做的那样),您将需要调用$timeout.flush()您的测试以确保它运行。

我最终不得不在我的测试中运行以下命令以使其正常工作:

// Re-digest to ensure we see the changes
scope.$digest();

// Flush any pending $timeout tasks to ensure we see changes
try {
  this.$timeout.verifyNoPendingTasks();
} catch ( aException ) {
  this.$timeout.flush();
}
于 2014-12-23T17:26:38.697 回答
1

我认为这是因为角度错误。你可以在这里看到

一种解决方法是删除模板 url 并在指令本身中使用模板或使用 $templateCache.get('/app/partials/my_directive.html')

这对我有用:)

于 2013-11-13T13:57:44.917 回答
-1

$watch应该在这种情况下工作。试试看

于 2014-12-22T09:29:27.607 回答