2013-07-14 36 views
21

我有一个简单的指令,模板在其中使用ng-repeat。我需要运行一些代码来针对由ng-repeat指令创建的一些元素实例化jquery组件。问题是,如果我把这个代码放在链接函数中。 ng-repeat尚未构建这些元素,因此没有实例化。AngularJS:链接到使用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> 

我使用$表()和$观察尝试()时域改变和实例工具提示代码,然后注册一个回调。但是,我似乎无法在正确的时间给我打电话。任何想法我失踪?

+0

也许你将迫使链接功能编译。 element = angular.element($ compile(template)(scope)); – dimirc

+0

如果问题正在等待dom渲染,则可以将链接函数项目包装在$ timeout中。 '$超时(函数(){element.find(布拉布拉); element.find(foofoo);},0)'。如果这不是修复,那么问题不在于dom渲染。 – rGil

+3

似乎这将是一个普遍的问题,所以我不得不认为有更好的方法来实现这一点,而不诉诸像使用超时黑客。 – chubbsondubs

回答

23

我发现如果创建了另一个指令,我添加到ng-repeat被创建的元素,它会通知每个元素重复创建。然后,我可以简单$发出父指令可以侦听的事件。在那一点上它可以执行链接到那里的重复元素。这非常适用于dom中的多重ng重复,因为我可以通过它们的事件类型将它们分开,并将它们传递给新指令。

这里是我的指令:

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(...); 
    }); 
} 
+0

谢谢,这是一个非常有用的答案。我有一个相关的问题:是否有一个标准模式来检测所有的重复都完成了?我可能不想为每个添加的孩子开出一个活动,但只完成一次。 – mariachimike

+4

您可以在指令中放入if语句,仅在最后一个scope.it == scope。$ last上触发。 – chubbsondubs

+0

这非常有帮助 - 谢谢! – mariachimike

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来证明这一点的解决方案。

+0

感谢您的回答,但我发现了一个更强大的处理方法。 – chubbsondubs

+0

谢谢。我试图追踪4 ng-repeats何时完成渲染,这比指令chubbsondubs的指令方法更适合。 – scottt732

+2

我也更喜欢这种方法 - 似乎矫枉过正让我有另一个指令。谢谢。 –

1

我认为这是由于角度bug。你可以看到这个here

一个解决方法是删除模板网址,并使用该模板的指令本身或使用$ templateCache.get(“/应用/谐音/ my_directive.html”)

这为我工作:)

-1

$watch应该在这方面工作。试试吧

2

我刚刚遇到了这个确切的问题,并发现我认为是一个更好的解决方案。基本上,您可以使用$timeoutng-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具有live demo,显示它的行动。

部分字词:如果您正在使用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(); 
} 
+0

使用$ timeout永远不是一个更好的解决方案,因为您可以正确使用事件。使用定时器非常依赖运行代码的底层平台。这意味着它可能非常不可靠,因为手机CPU不像桌面一样,并且取决于CPU可能发生的情况。所以它可能会工作一次,然后不工作,因为CPU是在做一些激烈的事情。底线是不可靠的。更不用说你通常必须扮演非常保守的角色,以确保你不会在重复渲染的中间不小心点燃它,所以它总是比较慢。 – chubbsondubs

+0

@chubbsondubs我一般同意,但这个'$超时'的意义不在于等待任何特定的时间;它只是将一个事件追加到当前'$ digest'循环的末尾,因此它可以在任何平台上工作,因为它不依赖于任何实际时间(因此毫秒数为0的原因)。这是否会改变您对此特定解决方案的看法?我总是喜欢反馈:-) –

+1

这是更好的解决方案imo。 – JayIsTooCommon

相关问题