Làm thế nào tôi có thể chạy một lệnh sau khi dom hoàn thành kết xuất?


115

Tôi đã có một vấn đề có vẻ đơn giản mà không có giải pháp rõ ràng (bằng cách đọc tài liệu Angular JS) .

Tôi đã nhận được một lệnh Angular JS thực hiện một số tính toán dựa trên chiều cao của các phần tử DOM khác để xác định chiều cao của vùng chứa trong DOM.

Một cái gì đó tương tự như thế này đang diễn ra bên trong chỉ thị:

return function(scope, element, attrs) {
    $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
}

Vấn đề là khi lệnh chạy $('site-header')không thể tìm thấy, trả về một mảng trống thay vì phần tử DOM được bao bọc bởi jQuery mà tôi cần.

Có một cuộc gọi lại mà tôi có thể sử dụng trong chỉ thị của mình chỉ chạy sau khi DOM đã được tải và tôi có thể truy cập các phần tử DOM khác thông qua các truy vấn kiểu chọn jQuery thông thường không?


1
Bạn có thể sử dụng scope. $ On () và scope. $ Emit () để sử dụng các sự kiện tùy chỉnh. Không chắc chắn liệu đây là cách tiếp cận đúng / được đề nghị mặc dù.
Tosh

Câu trả lời:


137

Nó phụ thuộc vào cách $ ('tiêu đề trang web') của bạn được xây dựng.

Bạn có thể thử sử dụng $ timeout với độ trễ 0. Cái gì đó như:

return function(scope, element, attrs) {
    $timeout(function(){
        $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
    });        
}

Giải thích cách thức hoạt động: một , hai .

Đừng quên tiêm $timeouttrong chỉ thị của bạn:

.directive('sticky', function($timeout)

5
Cảm ơn, tôi đã cố gắng để nó hoạt động trong nhiều năm cho đến khi tôi nhận ra rằng tôi đã không được thông qua $timeoutchỉ thị. Doh. Tất cả mọi thứ hoạt động bây giờ, chúc mừng.
Jannis

5
Có, bạn cần chuyển qua $timeoutchỉ thị như thế này:.directive('sticky', function($timeout) { return function (scope, element, attrs, controller) { $timeout(function(){ }); }); };
Vladimir Starkov

19
Các giải thích được liên kết của bạn giải thích lý do tại sao thủ thuật hết thời gian hoạt động trong JavaScript, nhưng không phải trong bối cảnh của AngularJS. Từ tài liệu chính thức : " [...] 4. Hàng đợi $ evalAsync được sử dụng để lên lịch công việc cần xảy ra bên ngoài khung ngăn xếp hiện tại, nhưng trước khi hiển thị chế độ xem của trình duyệt. Điều này thường được thực hiện với setTimeout (0) , nhưng Cách tiếp cận setTimeout (0) bị chậm và có thể gây ra hiện tượng nhấp nháy do trình duyệt hiển thị chế độ xem sau mỗi sự kiện. [...] "(nhấn mạnh của tôi)
Alberto

12
Tôi đang đối mặt với một vấn đề tương tự và nhận thấy rằng tôi cần khoảng 300ms để cho phép DOM tải trước khi thực hiện lệnh của mình. Tôi thực sự không thích cắm những con số có vẻ độc đoán như thế. Tôi chắc chắn tốc độ tải DOM sẽ thay đổi tùy thuộc vào người dùng. Vậy làm thế nào tôi có thể chắc chắn 300ms sẽ hoạt động cho bất cứ ai sử dụng ứng dụng của tôi?
keepitreal

5
không quá vui về câu trả lời này .. trong khi nó dường như trả lời câu hỏi của OP .. nó rất cụ thể đối với trường hợp của họ và nó liên quan đến dạng vấn đề chung hơn (nghĩa là chạy một lệnh sau khi tải dom) không rõ ràng + nó quá hack .. không có gì cụ thể về góc cạnh cả
abbood

44

Đây là cách tôi làm điều đó:

app.directive('example', function() {

    return function(scope, element, attrs) {
        angular.element(document).ready(function() {
                //MANIPULATE THE DOM
        });
    };

});

1
Thậm chí không cần angular.element vì phần tử đã có sẵn ở đó:element.ready(function(){
timhc22

1
Phần tử @ timhc22 là một tham chiếu đến DOMEuity đã kích hoạt lệnh này, đề xuất của bạn sẽ không dẫn đến tham chiếu DOMEuity cho các đối tượng Tài liệu trang.
tobius

Điều đó không hoạt động đúng. Tôi đang nhận được offsetWidth = 0 thông qua phương pháp này
Alexey Sh.

37

Có lẽ tác giả sẽ không cần câu trả lời của tôi nữa. Tuy nhiên, để hoàn thiện tôi cảm thấy những người dùng khác có thể thấy nó hữu ích. Giải pháp tốt nhất và đơn giản nhất là sử dụng $(window).load()bên trong cơ thể của hàm trả về. (ngoài ra bạn có thể sử dụng document.ready. Nó thực sự phụ thuộc vào việc bạn có cần tất cả các hình ảnh hay không).

Sử dụng $timeouttheo ý kiến ​​khiêm tốn của tôi là một lựa chọn rất yếu và có thể thất bại trong một số trường hợp.

Đây là mã hoàn chỉnh tôi sẽ sử dụng:

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function($scope, $elem, attrs){

           $(window).load(function() {
               //...JS here...
           });
       }
   }
});

1
Bạn có thể giải thích lý do tại sao nó "có thể thất bại trong một số trường hợp"? Những trường hợp nào bạn có ý nghĩa?
rryter

6
Bạn đang giả sử jQuery có sẵn ở đây.
Jonathan Cremin

3
@JonathanCremin Lựa chọn jQuery là vấn đề trong tầm tay theo OP
Nick Devereaux

1
Điều này hoạt động rất tốt tuy nhiên nếu có một bài viết xây dựng các mục mới bằng chỉ thị thì tải cửa sổ sẽ không kích hoạt sau tải ban đầu và do đó sẽ không hoạt động chính xác.
Brian Scott

@BrianScott - Tôi đã sử dụng kết hợp $ (window) .load để hiển thị trang ban đầu (trường hợp sử dụng của tôi đang chờ các tệp phông chữ nhúng) và sau đó Element. Sẵn sàng xử lý chuyển đổi chế độ xem.
aaaaaa

8

Có một ngcontentloadedsự kiện, tôi nghĩ bạn có thể sử dụng nó

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function(scope, elem, attrs){

                $$window = $ $window


                init = function(){
                    contentHeight = elem.outerHeight()
                    //do the things
                }

                $$window.on('ngcontentloaded',init)

       }
   }
});

21
Bạn có thể giải thích những gì $ $windowđang làm?
Cá trê

2
trông giống như một số loại cà phê, có thể nó có nghĩa là $ (cửa sổ $) và cửa sổ $ được đưa vào chỉ thị
mdob

5

Nếu bạn không thể sử dụng $ timeout do tài nguyên bên ngoài và không thể sử dụng một lệnh do vấn đề cụ thể với thời gian, hãy sử dụng phát sóng.

Thêm $scope.$broadcast("variable_name_here");sau khi tài nguyên bên ngoài mong muốn hoặc bộ điều khiển / chỉ thị chạy dài đã hoàn thành.

Sau đó thêm vào bên dưới sau khi tài nguyên bên ngoài của bạn đã được tải.

$scope.$on("variable_name_here", function(){ 
   // DOM manipulation here
   jQuery('selector').height(); 
}

Ví dụ như trong lời hứa về yêu cầu HTTP hoãn lại.

MyHttpService.then(function(data){
   $scope.MyHttpReturnedImage = data.image;
   $scope.$broadcast("imageLoaded");
});

$scope.$on("imageLoaded", function(){ 
   jQuery('img').height(80).width(80); 
}

2
Điều này sẽ không giải quyết được vấn đề, vì dữ liệu được tải không có nghĩa là chúng đã được hiển thị trong DOM, ngay cả khi chúng nằm trong các biến phạm vi thích hợp được liên kết với các phần tử DOM. Có một khoảng thời gian giữa thời điểm chúng được tải trong phạm vi và đầu ra được hiển thị trong dom.
René Stalder

1

Tôi đã có một vấn đề tương tự và muốn chia sẻ giải pháp của tôi ở đây.

Tôi có HTML sau:

<div data-my-directive>
  <div id='sub' ng-include='includedFile.htm'></div>
</div>

Vấn đề: Trong chức năng liên kết của chỉ thị của div cha, tôi muốn jquery'ing div div # sub. Nhưng nó chỉ cho tôi một đối tượng trống vì ng-gộp chưa hoàn thành khi chức năng liên kết của lệnh được chạy. Vì vậy, trước tiên tôi đã thực hiện một cách giải quyết bẩn với $ timeout, hoạt động nhưng tham số độ trễ phụ thuộc vào tốc độ máy khách (không ai thích điều đó).

Hoạt động nhưng bẩn:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        $timeout(function() {
            //very dirty cause of client-depending varying delay time 
            $('#sub').css(/*whatever*/);
        }, 350);
    };
    return directive;
}]);

Đây là giải pháp sạch:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        scope.$on('$includeContentLoaded', function() {
            //just happens in the moment when ng-included finished
            $('#sub').css(/*whatever*/);
        };
    };
    return directive;
}]);

Có lẽ nó giúp được ai đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.