Gửi sự kiện khi AngularJS tải xong


114

Tự hỏi đâu là cách tốt nhất để phát hiện kết thúc quá trình tải trang / khởi động, khi tất cả các lệnh đã hoàn tất việc biên dịch / liên kết.

Đã có sự kiện nào chưa? Tôi có nên làm quá tải hàm bootstrap không?

Câu trả lời:


204

Chỉ là một linh cảm: tại sao không nhìn vào cách lệnh ngCloak thực hiện nó? Rõ ràng chỉ thị ngCloak quản lý để hiển thị nội dung sau khi mọi thứ đã tải xong. Tôi cá rằng nhìn vào ngCloak sẽ dẫn đến câu trả lời chính xác ...

CHỈNH SỬA 1 giờ sau: Ok, tôi đã xem ngCloak và nó thực sự ngắn. Điều này rõ ràng ngụ ý rằng hàm biên dịch sẽ không được thực thi cho đến khi các biểu thức {{template}} được đánh giá (tức là mẫu mà nó đã tải), do đó, chức năng tốt của chỉ thị ngCloak.

Dự đoán của tôi là chỉ tạo một chỉ thị với cùng một sự đơn giản của ngCloak, sau đó trong hàm biên dịch của bạn, hãy làm bất cứ điều gì bạn muốn. :) Đặt chỉ thị trên phần tử gốc của ứng dụng của bạn. Bạn có thể gọi chỉ thị giống như myOnload và sử dụng nó làm thuộc tính my-onload. Hàm biên dịch sẽ thực thi khi mẫu đã được biên dịch (các biểu thức được đánh giá và các mẫu con được tải).

CHỈNH SỬA, 23 giờ sau: Ok, vì vậy tôi đã thực hiện một số nghiên cứu và tôi cũng đặt câu hỏi của riêng mình . Câu hỏi tôi hỏi có liên quan gián tiếp đến câu hỏi này, nhưng nó tình cờ dẫn tôi đến câu trả lời giải quyết câu hỏi này.

Câu trả lời là bạn có thể tạo một chỉ thị đơn giản và đặt mã của bạn trong hàm liên kết của chỉ thị, hàm này (đối với hầu hết các trường hợp sử dụng, được giải thích bên dưới) sẽ chạy khi phần tử của bạn đã sẵn sàng / được tải. Dựa trên mô tả của Josh về thứ tự thực thi các hàm biên dịch và liên kết ,

nếu bạn có đánh dấu này:

<div directive1>
  <div directive2>
    <!-- ... -->
  </div>
</div>

Sau đó, AngularJS sẽ tạo các chỉ thị bằng cách chạy các hàm chỉ thị theo một thứ tự nhất định:

directive1: compile
  directive2: compile
directive1: controller
directive1: pre-link
  directive2: controller
  directive2: pre-link
  directive2: post-link
directive1: post-link

Theo mặc định, một hàm "liên kết" thẳng là một liên kết sau, vì vậy, hàm liên kết bên ngoài của chỉ thị1 của bạn sẽ không chạy cho đến khi hàm liên kết của chỉ thị bên trong chạy xong. Đó là lý do tại sao chúng tôi nói rằng chỉ an toàn khi thực hiện thao tác DOM trong liên kết sau. Vì vậy, đối với câu hỏi ban đầu, sẽ không có vấn đề gì khi truy cập html bên trong của chỉ thị con từ hàm liên kết của chỉ thị bên ngoài, mặc dù nội dung được chèn động phải được biên dịch, như đã nói ở trên.

Từ điều này, chúng ta có thể kết luận rằng chúng ta có thể đơn giản tạo một chỉ thị để thực thi mã của chúng ta khi mọi thứ đã sẵn sàng / biên dịch / liên kết / tải:

    app.directive('ngElementReady', [function() {
        return {
            priority: -1000, // a low number so this directive loads after all other directives have loaded. 
            restrict: "A", // attribute only
            link: function($scope, $element, $attributes) {
                console.log(" -- Element ready!");
                // do what you want here.
            }
        };
    }]);

Bây giờ những gì bạn có thể làm là đặt chỉ thị ngElementReady vào phần tử gốc của ứng dụng và lệnh console.logsẽ kích hoạt khi nó được tải:

<body data-ng-app="MyApp" data-ng-element-ready="">
   ...
   ...
</body>

Nó đơn giản mà! Chỉ cần tạo một chỉ thị đơn giản và sử dụng nó. ;)

Bạn có thể tùy chỉnh thêm để nó có thể thực thi một biểu thức (tức là một hàm) bằng cách thêm $scope.$eval($attributes.ngElementReady);vào nó:

    app.directive('ngElementReady', [function() {
        return {
            priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
            restrict: "A",
            link: function($scope, $element, $attributes) {
                $scope.$eval($attributes.ngElementReady); // execute the expression in the attribute.
            }
        };
    }]);

Sau đó, bạn có thể sử dụng nó trên bất kỳ phần tử nào:

<body data-ng-app="MyApp" data-ng-controller="BodyCtrl" data-ng-element-ready="bodyIsReady()">
    ...
    <div data-ng-element-ready="divIsReady()">...<div>
</body>

Chỉ cần đảm bảo rằng bạn có các chức năng của mình (ví dụ: bodyIsReady và divIsReady) được xác định trong phạm vi (trong bộ điều khiển) mà phần tử của bạn nằm trong đó.

Lưu ý: Tôi đã nói điều này sẽ làm việc cho hầu hết các trường hợp. Hãy cẩn thận khi sử dụng một số lệnh như ngRepeat và ngIf. Họ tạo phạm vi của riêng họ và chỉ thị của bạn có thể không kích hoạt. Ví dụ: nếu bạn đặt chỉ thị ngElementReady mới của chúng tôi trên một phần tử cũng có ngIf và điều kiện của ngIf đánh giá là false, thì chỉ thị ngElementReady của chúng tôi sẽ không được tải. Hoặc, ví dụ: nếu bạn đặt chỉ thị ngElementReady mới của chúng tôi trên một phần tử cũng có chỉ thị ngInclude, chỉ thị của chúng tôi sẽ không được tải nếu mẫu cho ngInclude không tồn tại. Bạn có thể giải quyết một số vấn đề này bằng cách đảm bảo rằng bạn lồng các chỉ thị thay vì đặt tất cả chúng trên cùng một phần tử. Ví dụ, bằng cách thực hiện điều này:

<div data-ng-element-ready="divIsReady()">
    <div data-ng-include="non-existent-template.html"></div>
<div>

thay vì cái này:

<div data-ng-element-ready="divIsReady()" data-ng-include="non-existent-template.html"></div>

Chỉ thị ngElementReady sẽ được biên dịch trong ví dụ sau, nhưng hàm liên kết của nó sẽ không được thực thi. Lưu ý: các chỉ thị luôn được biên dịch, nhưng các chức năng liên kết của chúng không phải lúc nào cũng được thực thi tùy thuộc vào các trường hợp nhất định như ở trên.

CHỈNH SỬA, một vài phút sau:

Ồ, và để trả lời đầy đủ câu hỏi, bây giờ bạn có thể $emithoặc $broadcastsự kiện của mình từ biểu thức hoặc hàm được thực thi trong ng-element-readythuộc tính. :) Ví dụ:

<div data-ng-element-ready="$emit('someEvent')">
    ...
<div>

CHỈNH SỬA, thậm chí vài phút sau:

Câu trả lời của @ satchmorun cũng hoạt động, nhưng chỉ với tải ban đầu. Đây là một câu hỏi SO rất hữu ích mô tả thứ tự mà mọi thứ được thực thi bao gồm các hàm liên kết và các hàm app.runkhác. Vì vậy, tùy thuộc vào trường hợp sử dụng của bạn, app.runcó thể tốt, nhưng không tốt cho các phần tử cụ thể, trong trường hợp đó, các hàm liên kết tốt hơn.

CHỈNH SỬA, năm tháng sau, ngày 17 tháng 10 lúc 8:11 PST:

Điều này không hoạt động với các phần tử được tải không đồng bộ. Bạn sẽ cần thêm sổ sách kế toán vào các phần của mình (ví dụ: một cách là làm cho mỗi phần theo dõi thời điểm tải xong nội dung của nó, sau đó phát ra một sự kiện để phạm vi chính có thể đếm số lượng phần tử đã tải và cuối cùng làm những gì nó cần làm sau khi tất cả các phần được tải).

CHỈNH SỬA, ngày 23 tháng 10 lúc 10:52 tối theo giờ PST:

Tôi đã thực hiện một chỉ thị đơn giản để kích hoạt một số mã khi một hình ảnh được tải:

/*
 * This img directive makes it so that if you put a loaded="" attribute on any
 * img element in your app, the expression of that attribute will be evaluated
 * after the images has finished loading. Use this to, for example, remove
 * loading animations after images have finished loading.
 */
  app.directive('img', function() {
    return {
      restrict: 'E',
      link: function($scope, $element, $attributes) {
        $element.bind('load', function() {
          if ($attributes.loaded) {
            $scope.$eval($attributes.loaded);
          }
        });
      }
    };
  });

EDIT, ngày 24 tháng 10 lúc 12:48 sáng theo giờ PST:

Tôi đã cải thiện ngElementReadychỉ thị ban đầu của mình và đổi tên nó thành whenReady.

/*
 * The whenReady directive allows you to execute the content of a when-ready
 * attribute after the element is ready (i.e. done loading all sub directives and DOM
 * content except for things that load asynchronously like partials and images).
 *
 * Execute multiple expressions by delimiting them with a semi-colon. If there
 * is more than one expression, and the last expression evaluates to true, then
 * all expressions prior will be evaluated after all text nodes in the element
 * have been interpolated (i.e. {{placeholders}} replaced with actual values). 
 *
 * Caveats: if other directives exists on the same element as this directive
 * and destroy the element thus preventing other directives from loading, using
 * this directive won't work. The optimal way to use this is to put this
 * directive on an outer element.
 */
app.directive('whenReady', ['$interpolate', function($interpolate) {
  return {
    restrict: 'A',
    priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
    link: function($scope, $element, $attributes) {
      var expressions = $attributes.whenReady.split(';');
      var waitForInterpolation = false;

      function evalExpressions(expressions) {
        expressions.forEach(function(expression) {
          $scope.$eval(expression);
        });
      }

      if ($attributes.whenReady.trim().length == 0) { return; }

      if (expressions.length > 1) {
        if ($scope.$eval(expressions.pop())) {
          waitForInterpolation = true;
        }
      }

      if (waitForInterpolation) {
        requestAnimationFrame(function checkIfInterpolated() {
          if ($element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
            requestAnimationFrame(checkIfInterpolated);
          }
          else {
            evalExpressions(expressions);
          }
        });
      }
      else {
        evalExpressions(expressions);
      }
    }
  }
}]);

Ví dụ: sử dụng nó như thế này để kích hoạt someFunctionkhi một phần tử được tải và {{placeholders}}chưa được thay thế:

<div when-ready="someFunction()">
  <span ng-repeat="item in items">{{item.property}}</span>
</div>

someFunctionsẽ được gọi trước khi tất cả các item.propertytrình giữ chỗ được thay thế.

Đánh giá bao nhiêu biểu thức bạn muốn và thực hiện biểu thức cuối cùng trueđể chờ {{placeholders}}được đánh giá như sau:

<div when-ready="someFunction(); anotherFunction(); true">
  <span ng-repeat="item in items">{{item.property}}</span>
</div>

someFunctionanotherFunctionsẽ bị sa thải sau khi {{placeholders}}đã được thay thế.

Điều này chỉ hoạt động lần đầu tiên một phần tử được tải, không hoạt động trên các thay đổi trong tương lai. Nó có thể không hoạt động như mong muốn nếu $digesttiếp tục xảy ra sau khi các trình giữ chỗ đã được thay thế ban đầu (một thông báo $ có thể xảy ra tối đa 10 lần cho đến khi dữ liệu ngừng thay đổi). Nó sẽ phù hợp với hầu hết các trường hợp sử dụng.

EDIT, ngày 31 tháng 10 lúc 7:26 tối theo giờ PST:

Được rồi, đây có lẽ là bản cập nhật cuối cùng và cuối cùng của tôi. Điều này có thể sẽ hoạt động cho 99,999 trường hợp sử dụng ngoài đó:

/*
 * The whenReady directive allows you to execute the content of a when-ready
 * attribute after the element is ready (i.e. when it's done loading all sub directives and DOM
 * content). See: /programming/14968690/sending-event-when-angular-js-finished-loading
 *
 * Execute multiple expressions in the when-ready attribute by delimiting them
 * with a semi-colon. when-ready="doThis(); doThat()"
 *
 * Optional: If the value of a wait-for-interpolation attribute on the
 * element evaluates to true, then the expressions in when-ready will be
 * evaluated after all text nodes in the element have been interpolated (i.e.
 * {{placeholders}} have been replaced with actual values).
 *
 * Optional: Use a ready-check attribute to write an expression that
 * specifies what condition is true at any given moment in time when the
 * element is ready. The expression will be evaluated repeatedly until the
 * condition is finally true. The expression is executed with
 * requestAnimationFrame so that it fires at a moment when it is least likely
 * to block rendering of the page.
 *
 * If wait-for-interpolation and ready-check are both supplied, then the
 * when-ready expressions will fire after interpolation is done *and* after
 * the ready-check condition evaluates to true.
 *
 * Caveats: if other directives exists on the same element as this directive
 * and destroy the element thus preventing other directives from loading, using
 * this directive won't work. The optimal way to use this is to put this
 * directive on an outer element.
 */
app.directive('whenReady', ['$interpolate', function($interpolate) {
  return {
    restrict: 'A',
    priority: Number.MIN_SAFE_INTEGER, // execute last, after all other directives if any.
    link: function($scope, $element, $attributes) {
      var expressions = $attributes.whenReady.split(';');
      var waitForInterpolation = false;
      var hasReadyCheckExpression = false;

      function evalExpressions(expressions) {
        expressions.forEach(function(expression) {
          $scope.$eval(expression);
        });
      }

      if ($attributes.whenReady.trim().length === 0) { return; }

    if ($attributes.waitForInterpolation && $scope.$eval($attributes.waitForInterpolation)) {
        waitForInterpolation = true;
    }

      if ($attributes.readyCheck) {
        hasReadyCheckExpression = true;
      }

      if (waitForInterpolation || hasReadyCheckExpression) {
        requestAnimationFrame(function checkIfReady() {
          var isInterpolated = false;
          var isReadyCheckTrue = false;

          if (waitForInterpolation && $element.text().indexOf($interpolate.startSymbol()) >= 0) { // if the text still has {{placeholders}}
            isInterpolated = false;
          }
          else {
            isInterpolated = true;
          }

          if (hasReadyCheckExpression && !$scope.$eval($attributes.readyCheck)) { // if the ready check expression returns false
            isReadyCheckTrue = false;
          }
          else {
            isReadyCheckTrue = true;
          }

          if (isInterpolated && isReadyCheckTrue) { evalExpressions(expressions); }
          else { requestAnimationFrame(checkIfReady); }

        });
      }
      else {
        evalExpressions(expressions);
      }
    }
  };
}]);

Sử dụng nó như thế này

<div when-ready="isReady()" ready-check="checkIfReady()" wait-for-interpolation="true">
   isReady will fire when this {{placeholder}} has been evaluated
   and when checkIfReady finally returns true. checkIfReady might
   contain code like `$('.some-element').length`.
</div>

Tất nhiên, nó có thể được tối ưu hóa, nhưng tôi sẽ chỉ để nó ở mức đó. requestAnimationFrame thật tuyệt.


3
Thực sự khó chịu với tất cả những tiền tố "data-". Tôi rất vui vì tôi không sử dụng chúng.
stolsvik

1
@stolsvik heh, vâng, trong các trình duyệt hiện đại nhất, chúng không cần thiết.
trusktr

49
Xứng đáng bỏ phiếu cho lượng thời gian và nỗ lực dành cho câu trả lời này. Công việc tốt đẹp!
GordyD

8
Câu trả lời hay, nhưng vui lòng xem xét loại bỏ tất cả các dòng "Chỉnh sửa" và cấu trúc lại câu trả lời của bạn một chút. Lịch sử chỉnh sửa có sẵn thông qua liên kết "đã chỉnh sửa ..." ở cuối câu trả lời của bạn và nó làm mất tập trung khi đọc.
user247702

2
Mã nguồn sẽ thực sự hữu ích. Và nếu bạn có thể công khai nó trên npm, điều đó sẽ là HOÀN HẢO. Câu trả lời thực sự hay, được giải thích thực sự độc đáo, +1 cho số lượng nỗ lực đã bỏ ra cho việc này.
tfrascaroli

38

Trong tài liệu choangular.Module , có một mục mô tả runchức năng:

Sử dụng phương pháp này để đăng ký công việc sẽ được thực hiện khi kim phun tải xong tất cả các mô-đun.

Vì vậy, nếu bạn có một số mô-đun là ứng dụng của bạn:

var app = angular.module('app', [/* module dependencies */]);

Bạn có thể chạy nội dung sau khi các mô-đun đã được tải bằng:

app.run(function() {
  // Do post-load initialization stuff here
});

CHỈNH SỬA: Khởi tạo thủ công để giải cứu

Vì vậy, nó đã được chỉ ra rằng runkhông được gọi khi DOM đã sẵn sàng và được liên kết. Nó được gọi khi $injectormô-đun được tham chiếu bởi ng-appđã tải tất cả các phần phụ thuộc của nó, tách biệt với bước biên dịch DOM.

Tôi đã có một cái nhìn khác về khởi tạo thủ công và có vẻ như điều này sẽ thực hiện thủ thuật.

Tôi đã làm một trò đùa để minh họa .

HTML rất đơn giản:

<html>
    <body>
        <test-directive>This is a test</test-directive>
    </body>
</html>

Lưu ý thiếu một ng-app. Và tôi có một chỉ thị sẽ thực hiện một số thao tác DOM, vì vậy chúng tôi có thể đảm bảo thứ tự và thời gian của mọi thứ.

Như thường lệ, một mô-đun được tạo:

var app = angular.module('app', []);

Và đây là chỉ thị:

app.directive('testDirective', function() {
    return {
        restrict: 'E',
        template: '<div class="test-directive"><h1><div ng-transclude></div></h1></div>',
        replace: true,
        transclude: true,
        compile: function() {
            console.log("Compiling test-directive");
            return {
                pre: function() { console.log("Prelink"); },
                post: function() { console.log("Postlink"); }
            };
        }
    };
});

Chúng tôi sẽ thay thế test-directivethẻ bằng một divlớp test-directivevà bọc nội dung của nó trong một h1.

Tôi đã thêm một hàm biên dịch trả về cả hàm liên kết trước và sau để chúng ta có thể biết khi nào những thứ này chạy.

Đây là phần còn lại của mã:

// The bootstrapping process

var body = document.getElementsByTagName('body')[0];

// Check that our directive hasn't been compiled

function howmany(classname) {
    return document.getElementsByClassName(classname).length;
}

Trước khi chúng ta làm bất cứ điều gì, không nên có phần tử nào có lớp test-directivetrong DOM và sau khi chúng ta hoàn thành thì sẽ có 1.

console.log('before (should be 0):', howmany('test-directive'));

angular.element(document).ready(function() {
    // Bootstrap the body, which loades the specified modules
    // and compiled the DOM.
    angular.bootstrap(body, ['app']);

    // Our app is loaded and the DOM is compiled
    console.log('after (should be 1):', howmany('test-directive'));
});

Nó khá đơn giản. Khi tài liệu đã sẵn sàng, hãy gọi angular.bootstrapphần tử gốc của ứng dụng của bạn và một dãy tên mô-đun.

Trên thực tế, nếu bạn đính kèm một runhàm vào appmô-đun , bạn sẽ thấy nó được chạy trước khi bất kỳ quá trình biên dịch nào diễn ra.

Nếu bạn chạy fiddle và xem bảng điều khiển, bạn sẽ thấy như sau:

before (should be 0): 0 
Compiling test-directive 
Prelink
Postlink
after (should be 1): 1 <--- success!

2
cảm ơn @satchmorun! nhưng run () thực thi trước khi kết thúc phần liên kết - chỉ cần xác minh nó bằng một số console.logs.
Lior

được tò mò bản thân mình ... Tôi có một chỉ thị rằng đám cháy để thực hiện một số plugin jQuery DOM, runđám cháy trước khi chỉ thị và cháy khi chạy, html không phải là tất cả những gì
charlietfl

@charlietfl - Tôi đã đào sâu một chút về khởi động thủ công và đó thực sự là một cách khá dễ dàng để có được những gì câu hỏi đang tìm kiếm. Tôi đã thêm một chỉnh sửa khá dài cho câu trả lời ban đầu của mình.
satchmorun

5
tôi thấy rằng việc sử dụng $timeout( initMyPlugins,0)các công trình trong phạm vi chỉ thị của tôi, tất cả các html tôi cần là có
charlietfl

@satchmorun, xem phần tiếp theo sau: stackoverflow.com/questions/14989161/…
Lior

16

Angular chưa cung cấp cách báo hiệu khi một trang tải xong, có thể vì "kết thúc" tùy thuộc vào ứng dụng của bạn . Ví dụ: nếu bạn có cây phân cấp của các phần tử, một cây sẽ tải các phần khác. "Kết thúc" có nghĩa là tất cả chúng đã được tải. Bất kỳ khung công tác nào cũng sẽ gặp khó khăn khi phân tích mã của bạn và hiểu rằng mọi thứ đã xong hay vẫn còn chờ đợi. Đối với điều đó, bạn sẽ phải cung cấp logic dành riêng cho ứng dụng để kiểm tra và xác định điều đó.


14

Tôi đã đưa ra một giải pháp có thể đánh giá tương đối chính xác khi quá trình khởi tạo góc hoàn tất.

Chỉ thị là:

.directive('initialisation',['$rootScope',function($rootScope) {
            return {
                restrict: 'A',
                link: function($scope) {
                    var to;
                    var listener = $scope.$watch(function() {
                        clearTimeout(to);
                        to = setTimeout(function () {
                            console.log('initialised');
                            listener();
                            $rootScope.$broadcast('initialised');
                        }, 50);
                    });
                }
            };
        }]);

Sau đó, chỉ có thể thêm vào đó làm thuộc tính cho bodyphần tử và sau đó được lắng nghe để sử dụng$scope.$on('initialised', fn)

Nó hoạt động bằng cách giả định rằng ứng dụng được khởi tạo khi không còn chu kỳ $ thông báo nữa. $ watch được gọi là mọi chu kỳ thông báo và do đó một bộ đếm thời gian được bắt đầu (setTimeout không phải $ timeout nên một chu kỳ thông báo mới không được kích hoạt). Nếu một chu kỳ thông báo không xảy ra trong thời gian chờ thì ứng dụng được coi là đã khởi tạo.

Rõ ràng là nó không chính xác bằng giải pháp satchmoruns (vì có thể chu kỳ thông báo mất nhiều thời gian hơn thời gian chờ) nhưng giải pháp của tôi không cần bạn theo dõi các mô-đun, điều này giúp bạn dễ dàng quản lý hơn (đặc biệt là đối với các dự án lớn hơn ). Dù sao, có vẻ là đủ chính xác cho yêu cầu của tôi. Hy vọng nó giúp.


Giải pháp tuyệt vời. Đối với các dự án khi tất cả mã trong một hoặc hai tệp nén hoạt động rất tốt.
merqlove

1
Đây là giải pháp tuyệt vời. Trong trường hợp bạn có nhiều mã trong jquery và bạn đang cố gắng chuyển đổi mã sang dạng góc từng bước, điều này hoàn toàn hợp lý.
Mangesh Pimpalkar

11

Nếu bạn đang sử dụng Angular UI Router , bạn có thể lắng nghe $viewContentLoadedsự kiện.

"$ viewContentLoaded - được kích hoạt khi chế độ xem được tải, sau khi DOM được hiển thị . '$ scope' của chế độ xem sẽ tạo ra sự kiện." - Liên kết

$scope.$on('$viewContentLoaded', 
function(event){ ... });

3
$ scope. $ watch ('$ viewContentLoaded', function () đã thực hiện thủ thuật cho tôi
Louis XIV

2
phản đối cho 'điều mà bạn nên là'. Điều gì sẽ xảy ra nếu tôi nói "nếu bạn đang sử dụng React thay vì Angular (bạn nên sử dụng) ..."? Không phải là một thái độ rất cần có trong IMHO hệ sinh thái này.
Valentin Waeselynck,

@ValentinWaeselynck bạn hoàn toàn đúng. Tôi đã chỉnh sửa câu trả lời của mình để xóa thành kiến ​​của mình.
Jordan Skole

1
Đã làm cho tôi! Cảm ơn bạn. Tôi thực sự đã thêm nó vào hàm chạy của mình, sau đó thay đổi $ scope thành $ rootScope.
JDavies

2
Như Đại học Angular đã chỉ ra trong một câu trả lời khác, $ viewContentLoaded có thể không có ở đó ban đầu, nhưng bây giờ nó hoạt động trong trình cung cấp ngRoute được tích hợp sẵn, theo cùng một cách. Với suy nghĩ đó, tôi nghĩ đây là câu trả lời nhanh chóng, đơn giản, dễ đọc mà nhiều (hầu hết?) Độc giả tương lai sẽ tìm kiếm.
Kevin Crumley

3

tôi quan sát thao tác DOM của góc với JQuery và tôi đã thiết lập kết thúc cho ứng dụng của mình (một số loại tình huống được xác định trước và thỏa đáng mà tôi cần cho ứng dụng trừu tượng của mình) ví dụ: tôi mong đợi ng-lặp lại của mình tạo ra kết quả 7 và ở đó cho tôi sẽ thiết lập một chức năng quan sát với sự trợ giúp của setInterval cho mục đích này.

$(document).ready(function(){

  var interval = setInterval(function(){

  if($("article").size() == 7){
     myFunction();
     clearInterval(interval);
  }

  },50);

});

3
Tôi sẽ không làm điều này. Sử dụng các khoảng thời gian để kiểm tra mọi thứ đang xảy ra không phải là phương pháp hay, không thể điều chỉnh được và có nhiều cách khác để khiến mọi thứ diễn ra. Bộ hẹn giờ dành cho việc thực hiện các nhiệm vụ cụ thể cần diễn ra sau một khoảng thời gian nhất định, không phải để "đoán" khi nào nội dung hoặc kết quả đã sẵn sàng.
dudewad

Chưa kể rằng việc sử dụng bộ đếm thời gian jquery đối với nền tảng góc cạnh là phản năng suất - góc có một lớp thời gian chờ và bạn nên sử dụng lớp đó nếu không bạn đang xếp hai khung công tác và nó trở nên khó hiểu rất nhanh.
dudewad

3

Nếu bạn không sử dụng module ngRoute , tức là bạn không có sự kiện $ viewContentLoaded .

Bạn có thể sử dụng một phương pháp chỉ thị khác:

    angular.module('someModule')
        .directive('someDirective', someDirective);

    someDirective.$inject = ['$rootScope', '$timeout']; //Inject services

    function someDirective($rootScope, $timeout){
        return {
            restrict: "A",
            priority: Number.MIN_SAFE_INTEGER, //Lowest priority
            link    : function(scope, element, attr){
                $timeout(
                    function(){
                        $rootScope.$emit("Some:event");
                    }
                );
            }
        };
    }

Theo đó câu trả lời của trusktr nó có mức ưu tiên thấp nhất. Cộng với $ timeout sẽ khiến Angular chạy qua toàn bộ vòng lặp sự kiện trước khi thực thi callback.

$ rootScope được sử dụng, vì nó cho phép đặt chỉ thị trong bất kỳ phạm vi nào của ứng dụng và chỉ thông báo cho những người nghe cần thiết.

$ rootScope. $ release sẽ kích hoạt một sự kiện cho tất cả $ rootScope. $ chỉ trên các trình nghe. Phần thú vị là $ rootScope. $ Broadcast sẽ thông báo cho tất cả $ rootScope. $ Trên cũng như $ scope. $ Trên người nghe Nguồn


2

Theo nhóm Angular và vấn đề Github này :

bây giờ chúng ta có các sự kiện $ viewContentLoaded và $ includeContentLoaded được phát ra trong ng-view và ng-include tương ứng. Tôi nghĩ rằng điều này gần như mọi người có thể biết khi nào chúng ta hoàn thành việc biên dịch.

Dựa trên điều này, có vẻ như điều này hiện không thể thực hiện một cách đáng tin cậy, nếu không Angular sẽ cung cấp sự kiện ra khỏi hộp.

Khởi động ứng dụng ngụ ý chạy chu trình thông báo trên phạm vi gốc và cũng không có sự kiện kết thúc chu trình thông báo.

Theo tài liệu thiết kế Angular 2 :

Bởi vì nhiều lần tiêu hóa, không thể xác định và thông báo cho thành phần rằng mô hình đang ổn định. Điều này là do thông báo có thể thay đổi thêm dữ liệu, có thể khởi động lại quá trình liên kết.

Theo điều này, thực tế là điều này không thể xảy ra là một trong những lý do tại sao quyết định viết lại trong Angular 2.


2

Tôi có một phân đoạn được tải vào sau / bởi phần chính đến qua định tuyến.

Tôi cần chạy một hàm sau khi phần phụ đó được tải và tôi không muốn viết một chỉ thị mới và nhận ra rằng bạn có thể sử dụng một ngIf

Bộ phận kiểm soát của phụ huynh:

$scope.subIsLoaded = function() { /*do stuff*/; return true; };

HTML của quảng cáo phụ

<element ng-if="subIsLoaded()"><!-- more html --></element>

1

Nếu bạn muốn tạo JS với dữ liệu phía máy chủ (JSP, PHP), bạn có thể thêm logic của mình vào một dịch vụ, logic này sẽ được tải tự động khi bộ điều khiển của bạn được tải.

Ngoài ra, nếu bạn muốn phản ứng khi tất cả các lệnh được biên dịch / liên kết xong, bạn có thể thêm các giải pháp được đề xuất thích hợp ở trên trong logic khởi tạo.

module.factory('YourControllerInitService', function() {

    // add your initialization logic here

    // return empty service, because it will not be used
    return {};
});


module.controller('YourController', function (YourControllerInitService) {
});

0

Đây là tất cả các giải pháp tuyệt vời, Tuy nhiên, nếu bạn hiện đang sử dụng Định tuyến thì tôi thấy giải pháp này là giải pháp dễ nhất và ít mã nhất cần thiết. Sử dụng thuộc tính 'giải quyết' để đợi lời hứa hoàn thành trước khi kích hoạt tuyến đường. ví dụ

$routeProvider
.when("/news", {
    templateUrl: "newsView.html",
    controller: "newsController",
    resolve: {
        message: function(messageService){
            return messageService.getMessage();
    }
}

})

Nhấp vào đây để xem toàn bộ tài liệu - Tín dụng cho K. Scott Allen


0

có thể tôi có thể giúp bạn bằng ví dụ này

Trong hộp ưa thích tùy chỉnh, tôi hiển thị nội dung với các giá trị nội suy.

trong dịch vụ, trong phương pháp hộp thư "mở", tôi làm

open: function(html, $compile) {
        var el = angular.element(html);
     var compiledEl = $compile(el);
        $.fancybox.open(el); 
      }

$ compile trả về dữ liệu đã biên dịch. bạn có thể kiểm tra dữ liệu đã biên dịch

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.