Có bài đăng kết xuất gọi lại cho chỉ thị Angular JS không?


139

Tôi vừa nhận được chỉ thị của mình để lấy một mẫu để nối vào phần tử của nó như thế này:

# CoffeeScript
.directive 'dashboardTable', ->
  controller: lineItemIndexCtrl
  templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
  (scope, element, attrs) ->
    element.parent('table#line_items').dataTable()
    console.log 'Just to make sure this is run'

# HTML
<table id="line_items">
    <tbody dashboard-table>
    </tbody>
</table>

Tôi cũng đang sử dụng một Plugin jQuery có tên là DataTables. Cách sử dụng chung của nó là như thế này: $ ('bảng # some_id'). DataTable (). Bạn có thể chuyển dữ liệu JSON vào lệnh gọi dataTable () để cung cấp dữ liệu bảng HOẶC bạn có thể có dữ liệu trên trang và nó sẽ làm phần còn lại .. Tôi đang thực hiện sau, có các hàng đã có trên trang HTML .

Nhưng vấn đề là tôi phải gọi dataTable () trên bảng # line_items SAU DOM đã sẵn sàng. Lệnh của tôi ở trên gọi phương thức dataTable () TRƯỚC KHI mẫu được gắn vào phần tử của lệnh. Có cách nào để tôi có thể gọi các chức năng SAU khi nối thêm không?

Cảm ơn sự giúp đỡ của bạn!

CẬP NHẬT 1 sau câu trả lời của Andy:

Tôi muốn đảm bảo rằng phương thức liên kết chỉ được gọi là SAU mọi thứ đều có trên trang vì vậy tôi đã thay đổi chỉ thị cho một thử nghiệm nhỏ:

# CoffeeScript
#angular.module(...)
.directive 'dashboardTable', ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.find('#sayboo').html('boo')

      controller: lineItemIndexCtrl
      template: "<div id='sayboo'></div>"

    }

Và tôi thực sự thấy "boo" trong div # sayboo.

Sau đó, tôi thử cuộc gọi dữ liệu jquery của tôi

.directive 'dashboardTable',  ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.parent('table').dataTable() # NEW LINE

      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }

Không có may mắn ở đó

Sau đó, tôi cố gắng thêm thời gian chờ:

.directive 'dashboardTable', ($timeout) ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        $timeout -> # NEW LINE
          element.parent('table').dataTable()
        ,5000
      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }

Và nó hoạt động. Vì vậy, tôi tự hỏi những gì sai trong phiên bản không hẹn giờ của mã?


1
@adardesign Không tôi chưa bao giờ làm, tôi phải sử dụng đồng hồ bấm giờ. Vì một số lý do, gọi lại không phải là một cuộc gọi lại ở đây, thực sự. Tôi có một bảng có 11 cột và 100 hàng, do đó, góc cạnh tự nhiên trông giống như một đặt cược tốt để sử dụng để liên kết dữ liệu; nhưng tôi cũng cần sử dụng plugin jquery Datatables đơn giản như $ ('bảng'). datable (). Sử dụng chỉ thị hoặc chỉ có một đối tượng json câm với tất cả các hàng và sử dụng ng-repeat để lặp lại, tôi không thể lấy $ (). Datizable () để chạy SAU phần tử html của bảng được hiển thị, vì vậy hiện tại mẹo của tôi là hẹn giờ để kiểm tra xem $ ('tr'). length> 3 (b / c của tiêu đề / chân trang)
Nik So

2
@adardesign Và vâng, tôi đã thử tất cả phương thức biên dịch, phương thức biên dịch trả về một đối tượng có chứa phương thức postLink / preLink, phương thức biên dịch trả về chỉ một hàm (cụ thể là hàm liên kết), phương thức liên kết (không có phương thức biên dịch vì theo như tôi có thể nói, nếu bạn có một phương thức biên dịch trả về một phương thức liên kết, thì hàm liên kết sẽ bị bỏ qua) .. Không có công việc nào phải phụ thuộc vào thời gian chờ $ cũ tốt. Sẽ cập nhật bài đăng này nếu tôi tìm thấy bất cứ điều gì hoạt động tốt hơn hoặc đơn giản là khi tôi thấy rằng cuộc gọi lại thực sự hoạt động như gọi lại
Nik So

Câu trả lời:


215

Nếu tham số thứ hai, "độ trễ" không được cung cấp, hành vi mặc định là thực thi chức năng sau khi DOM hoàn thành kết xuất. Vì vậy, thay vì setTimeout, hãy sử dụng $ timeout:

$timeout(function () {
    //DOM has finished rendering
});

8
Tại sao nó không được giải thích trong các tài liệu ?
Gaui

23
Bạn nói đúng, câu trả lời của tôi hơi sai lệch vì tôi đã cố gắng làm cho nó đơn giản. Câu trả lời đầy đủ là hiệu ứng này không phải là kết quả của Angular mà là trình duyệt. $timeout(fn)cuối cùng các cuộc gọi setTimeout(fn, 0)có tác dụng làm gián đoạn quá trình thực thi Javascript và cho phép trình duyệt hiển thị nội dung trước, trước khi tiếp tục thực thi Javascript đó.
Quốc hội

7
Hãy nghĩ về trình duyệt như xếp hàng một số tác vụ nhất định, chẳng hạn như "thực thi javascript" và "kết xuất DOM", và setTimeout (fn, 0) nào sẽ đẩy "thực thi javascript" hiện đang chạy về phía sau hàng đợi, sau khi kết xuất .
Quốc hội

2
@GabLeRoux yup, điều đó sẽ có tác dụng tương tự ngoại trừ $ timeout có thêm lợi ích của việc gọi $ scope. $ Áp dụng () sau khi nó chạy. Với _.defer () bạn sẽ cần gọi nó theo cách thủ công nếu myFactor thay đổi các biến trên phạm vi.
quốc hội

2
Tôi đang có một kịch bản mà điều này không giúp được gì khi ở trang1 ng-repeat sẽ hiển thị một loạt các phần tử, sau đó tôi đi đến trang2 và sau đó tôi quay lại trang1 và tôi cố gắng đạt được các yếu tố ng-repeat mẹ ... nó trả về chiều cao sai. Nếu tôi hết thời gian chờ như 1000ms, thì nó hoạt động.
yodalr

14

Tôi đã có cùng một vấn đề và tôi tin rằng câu trả lời thực sự là không. Xem bình luận của Miško và một số thảo luận trong nhóm .

Angular có thể theo dõi rằng tất cả các hàm gọi nó để thao tác DOM đã hoàn tất, nhưng vì các hàm đó có thể kích hoạt logic async vẫn đang cập nhật DOM sau khi chúng quay trở lại, nên Angular không thể biết về nó. Bất kỳ cuộc gọi lại nào mà Angular đưa ra đôi khi có thể hoạt động, nhưng sẽ không an toàn khi dựa vào.

Chúng tôi đã giải quyết vấn đề này bằng một setTimeout, như bạn đã làm.

(Xin lưu ý rằng không phải ai cũng đồng ý với tôi - bạn nên đọc các nhận xét về các liên kết ở trên và xem bạn nghĩ gì.)


7

Bạn có thể sử dụng chức năng 'liên kết', còn được gọi là postLink, chạy sau khi mẫu được đưa vào.

app.directive('myDirective', function() {
  return {
    link: function(scope, elm, attrs) { /*I run after template is put in */ },
    template: '<b>Hello</b>'
  }
});

Hãy đọc nó nếu bạn có kế hoạch thực hiện các chỉ thị, đó là một sự trợ giúp lớn: http://docs.angularjs.org/guide/directive


Xin chào Andy, cảm ơn bạn rất nhiều vì đã trả lời; Tôi đã thử chức năng liên kết nhưng tôi không ngại thử lại chính xác như bạn mã hóa nó; Tôi đã dành 1,5 ngày qua để đọc lên trang chỉ thị đó; và nhìn vào các ví dụ trên trang web của góc. Sẽ thử mã của bạn bây giờ.
Nik So

Ah, tôi thấy bây giờ bạn đang cố gắng thực hiện liên kết nhưng bạn đã làm sai. Nếu bạn chỉ trả về một hàm, nó được coi là liên kết. Nếu bạn trả về một đối tượng, bạn phải trả lại nó với khóa là 'liên kết'. Bạn cũng có thể trả về một hàm liên kết từ hàm biên dịch của bạn.
Andrew Joslin

Xin chào Andy, đã lấy lại kết quả của tôi; Tôi gần như mất đi sự tỉnh táo của mình, bởi vì tôi thực sự đã làm về cơ bản câu trả lời của bạn ở đây là gì. Vui lòng xem cập nhật của tôi
Nik So

Humm, hãy thử một cái gì đó như: <table id = "bob"> <tbody dashboard-table = "# bob"> </ tbody> </ table> Sau đó, trong liên kết của bạn, hãy làm $ (attrs.dashboardTable) .dataTable () để hãy chắc chắn rằng nó được chọn đúng. Hoặc tôi đoán bạn đã thử điều đó .. Tôi thực sự không chắc chắn nếu liên kết không hoạt động.
Andrew Joslin

Cái này hoạt động với tôi, tôi muốn di chuyển các phần tử trong kết xuất mẫu bài đăng theo yêu cầu của tôi, đã làm điều đó trong chức năng liên kết
Cảm ơn

7

Mặc dù câu trả lời của tôi không liên quan đến các cơ sở dữ liệu, nhưng nó giải quyết vấn đề thao tác DOM và ví dụ: khởi tạo plugin jQuery cho các lệnh được sử dụng trên các phần tử có nội dung được cập nhật theo cách không đồng bộ.

Thay vì thực hiện thời gian chờ, người ta chỉ có thể thêm một chiếc đồng hồ sẽ lắng nghe những thay đổi nội dung (hoặc thậm chí bổ sung các kích hoạt bên ngoài).

Trong trường hợp của tôi, tôi đã sử dụng cách giải quyết này để khởi tạo plugin jQuery sau khi lặp lại ng đã tạo ra DOM bên trong của tôi - trong trường hợp khác tôi đã sử dụng nó để chỉ thao tác DOM sau khi thuộc tính scope bị thay đổi tại bộ điều khiển. Đây là cách tôi đã làm ...

HTML:

<div my-directive my-directive-watch="!!myContent">{{myContent}}</div>

JS:

app.directive('myDirective', [ function(){
    return {
        restrict : 'A',
        scope : {
            myDirectiveWatch : '='
        },
        compile : function(){
            return {
                post : function(scope, element, attributes){

                    scope.$watch('myDirectiveWatch', function(newVal, oldVal){
                        if (newVal !== oldVal) {
                            // Do stuff ...
                        }
                    });

                }
            }
        }
    }
}]);

Lưu ý: Thay vì chỉ truyền biến myContent thành bool tại thuộc tính my-directive-watch, người ta có thể tưởng tượng bất kỳ biểu thức tùy ý nào ở đó.

Lưu ý: Việc cách ly phạm vi như trong ví dụ trên chỉ có thể được thực hiện một lần cho mỗi phần tử - cố gắng thực hiện điều này với nhiều lệnh trên cùng một phần tử sẽ dẫn đến $ compile: multidir Error - xem: https://docs.angularjs.org / lỗi / $ biên dịch / multidir


7

Có thể là muộn để trả lời câu hỏi này. Nhưng vẫn có ai đó có thể nhận được lợi ích từ câu trả lời của tôi.

Tôi đã gặp vấn đề tương tự và trong trường hợp của tôi, tôi không thể thay đổi chỉ thị vì đó là thư viện và thay đổi mã của thư viện không phải là một thực tiễn tốt. Vì vậy, những gì tôi đã làm là sử dụng một biến để chờ tải trang và sử dụng ng-if bên trong html của tôi để chờ kết xuất phần tử cụ thể.

Trong bộ điều khiển của tôi:

$scope.render=false;

//this will fire after load the the page

angular.element(document).ready(function() {
    $scope.render=true;
});

Trong html của tôi (trong trường hợp thành phần html của tôi là một canvas)

<canvas ng-if="render"> </canvas>

3

Tôi đã cùng một vấn đề, nhưng sử dụng góc + DataTable với một fnDrawCallback+ nhóm hàng + $ chỉ lồng nhau biên soạn. Tôi đã đặt $ timeout trong fnDrawCallbackhàm của mình để sửa lỗi hiển thị phân trang.

Trước ví dụ, dựa trên nguồn row_grouping:

var myDrawCallback = function myDrawCallbackFn(oSettings){
  var nTrs = $('table#result>tbody>tr');
  for(var i=0; i<nTrs.length; i++){
     //1. group rows per row_grouping example
     //2. $compile html templates to hook datatable into Angular lifecycle
  }
}

Sau ví dụ:

var myDrawCallback = function myDrawCallbackFn(oSettings){
  var nTrs = $('table#result>tbody>tr');
  $timeout(function requiredRenderTimeoutDelay(){
    for(var i=0; i<nTrs.length; i++){
       //1. group rows per row_grouping example
       //2. $compile html templates to hook datatable into Angular lifecycle
    }
  ,50); //end $timeout
}

Ngay cả một khoảng thời gian chờ ngắn cũng đủ để cho phép Angular đưa ra các chỉ thị Angular đã biên dịch của tôi.


Chỉ tò mò, bạn có một cái bàn khá lớn với nhiều cột không? bởi vì tôi đã thấy rằng tôi cần một sự phiền toái rất nhiều mili giây (> 100) vì vậy đừng để cuộc gọi dataTable () bị sặc
Nik Vì vậy,

Tôi đã tìm thấy sự cố xảy ra khi điều hướng trang DataTable cho tập kết quả gồm 2 hàng thành hơn 150 hàng. Vì vậy, không - tôi không nghĩ kích thước của bảng là vấn đề, nhưng có lẽ DataTable đã thêm đủ chi phí kết xuất để nhai một vài phần nghìn giây đó. Trọng tâm của tôi là tập hợp nhóm để hoạt động trong DataTable với tích hợp AngularJS tối thiểu.
JJ Zabkar

2

Không có giải pháp nào hiệu quả đối với tôi chấp nhận sử dụng thời gian chờ. Điều này là do tôi đang sử dụng một mẫu được tạo động trong quá trình postLink.

Tuy nhiên, xin lưu ý, có thể có thời gian chờ là '0' vì thời gian chờ thêm chức năng được gọi vào hàng đợi của trình duyệt sẽ xuất hiện sau công cụ kết xuất góc vì điều này đã có trong hàng đợi.

Tham khảo điều này: http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering


0

Dưới đây là một lệnh để có các hành động được lập trình sau khi kết xuất nông. Bằng cách nông, tôi có nghĩa là nó sẽ đánh giá sau khi chính yếu tố đó được kết xuất và điều đó sẽ không liên quan đến khi nào nội dung của nó được hiển thị. Vì vậy, nếu bạn cần một số yếu tố phụ thực hiện hành động kết xuất bài đăng, bạn nên xem xét sử dụng nó ở đó:

define(['angular'], function (angular) {
  'use strict';
  return angular.module('app.common.after-render', [])
    .directive('afterRender', [ '$timeout', function($timeout) {
    var def = {
        restrict : 'A', 
        terminal : true,
        transclude : false,
        link : function(scope, element, attrs) {
            if (attrs) { scope.$eval(attrs.afterRender) }
            scope.$emit('onAfterRender')
        }
    };
    return def;
    }]);
});

sau đó bạn có thể làm:

<div after-render></div>

hoặc với bất kỳ biểu thức hữu ích nào như:

<div after-render="$emit='onAfterThisConcreteThingRendered'"></div>


Điều này không thực sự sau khi nội dung được hiển thị. Nếu tôi có một biểu thức bên trong phần tử <div after-render> {{blah}} </ div> thì tại thời điểm này, biểu thức chưa được đánh giá. Nội dung của div vẫn là {{blah}} bên trong hàm liên kết. Vì vậy, về mặt kỹ thuật, bạn đang kích hoạt sự kiện trước khi nội dung được hiển thị.
Edward Olamisan

Đây là một hành động nông cạn sau khi kết xuất, tôi chưa bao giờ tuyên bố nó là sâu
Sebastian Sastre

0

Tôi đã làm việc này với chỉ thị sau:

app.directive('datatableSetup', function () {
    return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});

Và trong HTML:

<table class="table table-hover dataTable dataTable-columnfilter " datatable-setup="">

rắc rối chụp nếu ở trên không làm việc cho bạn.

1) lưu ý rằng 'datablesetup' tương đương với 'datable-setup'. Angular thay đổi định dạng thành trường hợp lạc đà.

2) đảm bảo rằng ứng dụng được xác định trước lệnh. ví dụ định nghĩa ứng dụng đơn giản và chỉ thị.

var app = angular.module('app', []);
app.directive('datatableSetup', function () {
    return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});

0

Theo sau thực tế là thứ tự tải không thể dự đoán được, một giải pháp đơn giản có thể được sử dụng.

Chúng ta hãy nhìn vào chỉ thị - 'người sử dụng mối quan hệ của chỉ thị'. Thông thường người sử dụng lệnh sẽ cung cấp một số dữ liệu cho lệnh hoặc sử dụng một số chức năng (chức năng) của các lệnh cung cấp. Mặt khác, lệnh này mong muốn một số biến được xác định trên phạm vi của nó.

Nếu chúng ta có thể đảm bảo rằng tất cả người chơi có tất cả các yêu cầu hành động của họ được thực hiện trước khi họ cố gắng thực hiện những hành động đó - mọi thứ sẽ ổn.

Và bây giờ chỉ thị:

app.directive('aDirective', function () {
    return {
        scope: {
            input: '=',
            control: '='
        },
        link: function (scope, element) {
            function functionThatNeedsInput(){
                //use scope.input here
            }
            if ( scope.input){ //We already have input 
                functionThatNeedsInput();
            } else {
                scope.control.init = functionThatNeedsInput;
            }
          }

        };
})

và bây giờ là người sử dụng lệnh html

<a-directive control="control" input="input"></a-directive>

và ở đâu đó trong bộ điều khiển của thành phần sử dụng lệnh:

$scope.control = {};
...
$scope.input = 'some data could be async';
if ( $scope.control.functionThatNeedsInput){
    $scope.control.functionThatNeedsInput();
}

Đó là về nó. Có rất nhiều chi phí nhưng bạn có thể mất $ timeout. Chúng tôi cũng giả định rằng thành phần sử dụng lệnh được khởi tạo trước lệnh này bởi vì chúng tôi phụ thuộc vào biến điều khiển để tồn tại khi lệnh được khởi tạo.

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.