2013-07-14 31 views
21

Tôi có một chỉ thị đơn giản trong đó mẫu sử dụng ng-lặp lại bên trong nó. Tôi cần chạy một số mã để khởi tạo một thành phần jquery đối với một số phần tử được tạo bởi lệnh ng-repeat. Vấn đề là nếu tôi đặt mã này trong hàm liên kết. Các ng-lặp lại đã không xây dựng những yếu tố nào được nêu ra vì vậy không có gì là instantiated.AngularJS: Liên kết đến các phần tử trong một chỉ thị sử dụng 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'}); 
    } 
    }; 
} 

Mẫu sẽ trông giống như sau. Tôi đang cố gắng đính kèm

<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> 

Tôi đã thử sử dụng $ watch() và $ observ() để đăng ký gọi lại khi miền thay đổi và khởi tạo mã công cụ sau đó. Tuy nhiên, tôi dường như không thể gọi cho tôi vào đúng thời điểm. Bất kỳ ý tưởng những gì tôi đang mất tích?

+0

Có lẽ bạn có thể buộc biên dịch từ chức năng liên kết. element = angular.element ($ compile (template) (scope)); – dimirc

+0

Nếu sự cố đang chờ hiển thị dom, bạn có thể bọc các mục chức năng liên kết của mình trong khoảng thời gian chờ $. '$ timeout (function() {element.find (blabla); element.find (foofoo);}, 0)'. Nếu đây không phải là sửa chữa, thì vấn đề không nằm ở việc dựng hình dom. – rGil

+3

Có vẻ như đây sẽ là một vấn đề phổ biến vì vậy tôi phải nghĩ rằng có những cách tốt hơn để thực hiện điều này mà không cần phải sử dụng để hack như sử dụng timeouts. – chubbsondubs

Trả lời

23

Tôi phát hiện ra rằng nếu tạo một chỉ thị khác mà tôi đã thêm vào phần tử trong đó ng-repeat được tạo, nó sẽ được thông báo cho từng phần tử lặp lại được tạo ra. Sau đó, tôi có thể chỉ đơn giản là $ phát ra một sự kiện mà chỉ thị cha mẹ có thể lắng nghe. Tại thời điểm đó nó có thể thực hiện liên kết với các yếu tố lặp đi lặp lại ở đó. Điều này làm việc khá độc đáo đặc biệt là cho nhiều ng-lặp lại trong dom vì tôi có thể tách chúng bằng loại sự kiện của họ mà sẽ được chuyển cho chỉ thị mới.

Dưới đây là chỉ thị của tôi:

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

Đây là việc sử dụng mà chỉ mới trong mẫu:

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

Sau đó, bên trong chỉ thị mẹ tôi có thể làm như sau:

link: function($scope, element, attributes) { 
    $scope.$on('domain_done', function(domainElement) { 
     domainElement.find('.someElementInsideARepeatedElement').click(...); 
    }); 
} 
+0

Cảm ơn, đây là một câu trả lời rất hữu ích. Tôi có một câu hỏi liên quan: có một mô hình chuẩn để phát hiện ra rằng tất cả các lần lặp lại được thực hiện? Tôi có thể không muốn kích hoạt sự kiện cho mọi trẻ em được thêm vào, nhưng chỉ một lần khi hoàn thành. – mariachimike

+4

Bạn có thể đặt câu lệnh if trong chỉ thị để chỉ kích hoạt trên scope scope cuối cùng .it ==. $ Last. – chubbsondubs

+0

Điều đó cực kỳ hữu ích - cảm ơn! – mariachimike

10

$watch sẽ có thể đạt được những gì bạn muốn làm:

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 
    }); 
} 

tôi đã tạo ra một plunk để chứng minh giải pháp này.

+0

Cảm ơn câu trả lời của bạn, nhưng tôi đã tìm ra cách xử lý mạnh mẽ hơn. – chubbsondubs

+0

Cảm ơn. Tôi đã cố gắng để theo dõi khi 4 ng-lặp lại đã được hoàn thành rendering và điều này là phù hợp hơn so với chỉ thị tiếp cận chỉ thị của chubbsondubs. – scottt732

+2

Tôi thích cách tiếp cận này tốt hơn - dường như quá mức với tôi để có một chỉ thị khác. Cảm ơn. –

1

Tôi nghĩ rằng điều này là do lỗi góc. Bạn có thể thấy điều này here

Một workaround là để loại bỏ các mẫu url và sử dụng mẫu trong chỉ thị riêng của mình hoặc sử dụng $ templateCache.get ('/ app/partials/my_directive.html')

này làm việc cho tôi :)

-1

$watch sẽ hoạt động trong ngữ cảnh này. hãy thử nó

2

Tôi chỉ gặp vấn đề chính xác này và tìm thấy những gì tôi tin là một giải pháp tốt hơn. Về cơ bản, bạn có thể sử dụng $timeout để xếp hàng tác vụ sau khi các ng-repeat có kết xuất xong.

Lưu ý: điều này không có nghĩa là bạn đang thực sự sử dụng bộ hẹn giờ hoặc bất kỳ loại thời gian trễ nào; nó chỉ là một cách đơn giản để nối thêm một hàm vào cuối vòng lặp $digest.

Dưới đây là trích đoạn mã của tôi.Chỉ thị sử dụng ng-repeat để hiển thị các tab, do đó, tôi phải đợi cho đến khi nó được hiển thị để chạy phương thức tabSelected() của chúng tôi, thêm lớp active vào phần tử 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); 
    } 
} 

Tôi tìm thấy giải pháp từ http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering trong đó có một live demo cho thấy nó trong hành động.

Từ chia tay: Nếu bạn đang thử nghiệm mã của mình (như bạn nên) với Karma, bạn sẽ cần gọi $timeout.flush() trong thử nghiệm của mình để đảm bảo rằng nó chạy.

tôi lên vết thương phải chạy sau trong thử nghiệm của tôi để làm cho nó hoạt động đúng:

// 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

Sử dụng $ timeout không bao giờ là giải pháp tốt hơn khi bạn có thể sử dụng sự kiện đúng cách. Việc sử dụng bộ tính giờ phụ thuộc quá nhiều vào nền tảng cơ bản đang chạy mã. Điều này có nghĩa là nó có thể rất không đáng tin cậy bởi vì một CPU điện thoại không giống như một máy tính để bàn và tùy thuộc vào những gì đang xảy ra trên CPU nó vẫn có thể vít lên. Vì vậy, nó có thể làm việc một lần sau đó không hoạt động vì CPU đã làm một cái gì đó dữ dội. Điểm mấu chốt là nó không đáng tin cậy. Chưa kể bạn thường phải chơi nó rất thận trọng để chắc chắn rằng bạn không vô tình bắn nó ở giữa lặp lại render vì vậy nó luôn luôn chậm hơn. – chubbsondubs

+0

@chubbsondubs Tôi đồng ý nói chung, nhưng điểm của '$ timeout' này không thực sự chờ đợi bất kỳ khoảng thời gian cụ thể nào; nó đơn giản gắn thêm một sự kiện vào cuối vòng lặp '$ digest' hiện tại, vì vậy nó sẽ hoạt động trên bất kỳ nền tảng nào vì nó không phụ thuộc vào bất kỳ thời gian thực tế nào (vì vậy tại sao số mili giây là 0). Điều này có thay đổi quan điểm của bạn về giải pháp cụ thể này không? Tôi luôn luôn yêu thích phản hồi :-) –

+1

Đây là giải pháp tốt hơn nhiều imo. – JayIsTooCommon

Các vấn đề liên quan