Làm thế nào để chạy chức năng trong bộ điều khiển AngularJS trên tài liệu đã sẵn sàng?


254

Tôi có một chức năng trong bộ điều khiển góc của mình, tôi muốn chức năng này được chạy trên tài liệu sẵn sàng nhưng tôi nhận thấy rằng góc chạy nó khi dom được tạo.

 function myController($scope)
 {
     $scope.init = function()
     {
        // I'd like to run this on document ready
     }

     $scope.init(); // doesn't work, loads my init before the page has completely loaded
 }

Bất cứ ai biết làm thế nào tôi có thể đi về điều này?

Câu trả lời:


449

Chúng ta có thể sử dụng angular.element(document).ready()phương pháp để đính kèm các cuộc gọi lại khi tài liệu đã sẵn sàng. Chúng ta chỉ có thể đính kèm gọi lại trong bộ điều khiển như vậy:

angular.module('MyApp', [])

.controller('MyCtrl', [function() {
    angular.element(document).ready(function () {
        document.getElementById('msg').innerHTML = 'Hello';
    });
}]);

http://jsfiddle.net/jgentes/stwyvq38/1/


64
hoặc bạn có thể sử dụng $ document
yet

29
Bạn sẽ cần phải tiêm $documentđể sử dụng nó. angular.elementsáng tạo.
nshew

17
Bạn sẽ cần phải tiêm $ document để sử dụng nó, nhưng đó là cách được khuyến nghị để tham chiếu phần tử tài liệu. Bạn không cần phải làm vậy, nhưng nó giúp cho việc kiểm tra dễ dàng hơn.
Jason Cox

10
documentlà một biến toàn cục trong đó có thể $documentđược tiêm bởi góc và do đó làm cho thử nghiệm dễ dàng hơn. Nói chung, rất khó để đơn vị các ứng dụng thử nghiệm truy cập các biến JS toàn cầu như tài liệu hoặc cửa sổ. Tôi cũng nghĩ rằng đó module.runlà một nơi tốt để đặt mã này thay vì bộ điều khiển.
lanoxx

7
Điều này không làm việc cho tôi, cuộc gọi getEuityById trả về null.
ThePartyTurtle

29

Xem bài đăng này Làm thế nào để thực hiện chức năng điều khiển góc khi tải trang?
Để tra cứu nhanh:

// register controller in html
<div data-ng-controller="myCtrl" data-ng-init="init()"></div>

// in controller
$scope.init = function () {
    // check if there is query in url
    // and fire search in case its value is not empty
};

Bằng cách này, bạn không phải đợi đến khi tài liệu sẵn sàng.


1
Nếu tôi cần gọi sau khi tải trang thì sao?
Rishi

22

Angular tự động khởi chạy theo DOMContentLoadedsự kiện hoặc khi tập lệnh angular.js được đánh giá nếu tại thời điểm đó tài liệu. YetState được đặt thành 'hoàn thành'. Tại thời điểm này, Angular tìm kiếm chỉ thị ng-app chỉ định gốc ứng dụng của bạn.

https://docs.angularjs.org/guide/bootstrap

Điều này có nghĩa là mã điều khiển sẽ chạy sau khi DOM sẵn sàng.

Vì vậy, nó chỉ $scope.init().


9
Tôi đã cố gắng để làm điều đó nhưng kỳ lạ là nó không hoạt động một số thứ trong trang của tôi không được tải
foreyez

và làm thế nào để biết khi nào mã góc đã sẵn sàng trước khi thực hiện các hành động như nhấp vào nút, v.v. khi viết các bài kiểm tra web được đánh giá cao?
akostadinov

Cũng DOMContentLoadedđược kích hoạt khi tài liệu đã được tải và phân tích cú pháp hoàn toàn, không cần chờ biểu định kiểu, hình ảnh và khung con để tải xong. Để biết thêm thông tin, hãy xem Sự khác biệt giữa các sự kiện DOMContentLoaded và load là gì? .
georgeawg

22

Angular có một số mốc thời gian để bắt đầu thực thi các chức năng. Nếu bạn tìm kiếm thứ gì đó như jQuery

$(document).ready();

Bạn có thể thấy tương tự này trong góc rất hữu ích:

$scope.$watch('$viewContentLoaded', function(){
    //do something
});

Điều này rất hữu ích khi bạn muốn thao tác các phần tử DOM. Nó sẽ bắt đầu thực hiện chỉ sau khi tất cả các phần tử te được tải.

CẬP NHẬT: Những gì được nói ở trên hoạt động khi bạn muốn thay đổi thuộc tính css. Tuy nhiên, đôi khi nó không hoạt động khi bạn muốn đo các thuộc tính của phần tử, chẳng hạn như chiều rộng, chiều cao, v.v. Trong trường hợp này, bạn có thể muốn thử điều này:

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});

Điều này xứng đáng nhiều hơn so với câu trả lời được đề xuất. Tôi đã nhận công việc này mà không sử dụng $ timeout.
Richie86

nó chỉ hoạt động với tôi với setTimeout (0) - ví dụ như không có nội dung theo lặp lại ng chưa được tải vào lúc này
Ignacio Vazquez

2

Tôi đã gặp tình huống tương tự khi tôi cần thực thi chức năng điều khiển sau khi chế độ xem được tải và sau khi một thành phần bên thứ 3 cụ thể trong chế độ xem được tải, khởi tạo và đã đặt tham chiếu đến chính nó trên phạm vi $. Điều cuối cùng làm việc với tôi là thiết lập một chiếc đồng hồ trên thuộc tính phạm vi này và chỉ kích hoạt chức năng của tôi sau khi nó được khởi tạo.

// $scope.myGrid property will be created by the grid itself
// The grid will have a loadedRows property once initialized

$scope.$watch('myGrid', function(newValue, oldValue) {
    if (newValue && newValue.loadedRows && !oldValue) {
        initializeAllTheGridThings();
    }
});

Người theo dõi được gọi là một vài lần với các giá trị không xác định. Sau đó, khi lưới được tạo và có thuộc tính mong đợi, chức năng khởi tạo có thể được gọi một cách an toàn. Lần đầu tiên người theo dõi được gọi với newValue không xác định, oldValue sẽ vẫn không được xác định.


1

Đây là nỗ lực của tôi bên trong bộ điều khiển bên ngoài bằng cách sử dụng coffeescript. Nó hoạt động khá tốt. Xin lưu ý rằng settings.screen.xs | sm | md | lg là các giá trị tĩnh được xác định trong một tệp không xấu xí mà tôi đưa vào ứng dụng. Các giá trị theo mỗi điểm dừng chính thức của Bootstrap 3 cho các kích thước truy vấn phương tiện cùng tên:

xs = settings.screen.xs // 480
sm = settings.screen.sm // 768
md = settings.screen.md // 992
lg = settings.screen.lg // 1200

doMediaQuery = () ->

    w = angular.element($window).width()

    $scope.xs = w < sm
    $scope.sm = w >= sm and w < md
    $scope.md = w >= md and w < lg
    $scope.lg = w >= lg
    $scope.media =  if $scope.xs 
                        "xs" 
                    else if $scope.sm
                        "sm"
                    else if $scope.md 
                        "md"
                    else 
                        "lg"

$document.ready () -> doMediaQuery()
angular.element($window).bind 'resize', () -> doMediaQuery()

1

Câu trả lời

$scope.$watch('$viewContentLoaded', 
    function() { 
        $timeout(function() {
            //do something
        },0);    
});

là cái duy nhất hoạt động trong hầu hết các kịch bản tôi đã thử nghiệm. Trong một trang mẫu có 4 thành phần, tất cả đều xây dựng HTML từ một mẫu, thứ tự các sự kiện là

$document ready
$onInit
$postLink
(and these 3 were repeated 3 more times in the same order for the other 3 components)
$viewContentLoaded (repeated 3 more times)
$timeout execution (repeated 3 more times)

Vì vậy, hầu hết các trường hợp $ document. Yet () là vô dụng vì DOM được xây dựng theo góc có thể không sẵn sàng.

Nhưng thú vị hơn, ngay cả sau khi $ viewContentLoaded bị bắn, yếu tố quan tâm vẫn không thể tìm thấy.

Chỉ sau khi $ timeout thực thi thì nó mới được tìm thấy. Lưu ý rằng mặc dù thời gian chờ $ là giá trị 0, gần 200 mili giây đã trôi qua trước khi nó được thực thi, chỉ ra rằng luồng này đã bị trì hoãn khá lâu, có lẽ trong khi DOM có các mẫu góc được thêm vào một luồng chính. Tổng thời gian từ $ document. Yet () đến lần thực hiện $ timeout cuối cùng là gần 500 mili giây.

Trong một trường hợp đặc biệt khi giá trị của một thành phần được đặt và sau đó giá trị text () được thay đổi sau khi hết thời gian chờ, giá trị $ timeout phải được tăng cho đến khi nó hoạt động (mặc dù có thể tìm thấy phần tử trong thời gian chờ $ ). Một cái gì đó không đồng bộ trong thành phần bên thứ 3 khiến giá trị được ưu tiên hơn văn bản cho đến khi đủ thời gian trôi qua. Một khả năng khác là $ scope. $ EvalAsync, nhưng không được thử.

Tôi vẫn đang tìm kiếm một sự kiện cho tôi biết DOM đã hoàn toàn ổn định và có thể được xử lý để mọi trường hợp đều hoạt động. Cho đến nay, một giá trị thời gian chờ tùy ý là cần thiết, có nghĩa là tốt nhất đây là một loại bùn có thể không hoạt động trên trình duyệt chậm. Tôi chưa thử các tùy chọn JQuery như liveQuery và xuất bản / đăng ký có thể hoạt động, nhưng chắc chắn không phải là góc cạnh thuần túy.


1

Nếu bạn đang nhận được một cái gì đó như thế getElementById call returns null, có lẽ là do chức năng đang chạy, nhưng ID đã không có thời gian để tải trong DOM.

Hãy thử sử dụng câu trả lời của Will (về phía trên) với độ trễ. Thí dụ:

angular.module('MyApp', [])

.controller('MyCtrl', [function() {
    $scope.sleep = (time) => {
        return new Promise((resolve) => setTimeout(resolve, time));
    };
    angular.element(document).ready(function () {
        $scope.sleep(500).then(() => {        
            //code to run here after the delay
        });
    });
}]);

0

Tại sao không thử với những gì tài liệu góc cạnh đề cập https://docs.angularjs.org/api/ng/feft/angular.element .

angular.element (gọi lại)

Tôi đã sử dụng cái này bên trong hàm $ onInit () {...}.

 var self = this;

 angular.element(function () {
        var target = document.getElementsByClassName('unitSortingModule');
        target[0].addEventListener("touchstart", self.touchHandler, false);
        ...
    });

Điều này làm việc cho tôi.


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.