Angular JS 지시문에 대한 포스트 렌더 콜백이 있습니까?


139

방금 다음과 같이 요소에 추가 할 템플릿을 가져 오는 지시문을 받았습니다.

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

또한 DataTables라는 jQuery 플러그인을 사용하고 있습니다. 일반적인 사용법은 $ ( 'table # some_id'). dataTable ()과 같습니다. JSON 데이터를 dataTable () 호출에 전달하여 테이블 데이터를 제공하거나 이미 페이지에 데이터를 보유하고 나머지를 수행 할 수 있습니다. 나머지는 HTML 페이지에 이미 있습니다. .

그러나 문제는 DOM 준비 후 table # line_items의 dataTable ()을 호출해야한다는 것입니다. 위의 지시문은 템플리트가 지시문 요소에 추가되기 전에 dataTable () 메소드를 호출합니다. 추가 후에 함수를 호출 할 수있는 방법이 있습니까?

도와 주셔서 감사합니다!

Andy의 답변 후 업데이트 1 :

링크 메소드가 페이지에있는 후에 만 ​​호출되도록하고 싶습니다. 그래서 약간의 테스트를 위해 지시문을 변경했습니다.

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

    }

그리고 div # sayboo에서 "boo"를 볼 수 있습니다.

그런 다음 jquery datatable 호출을 시도하십시오.

.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') %>"
    }

거기에 운이 없다

그런 다음 시간 초과를 추가하려고합니다.

.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') %>"
    }

그리고 그것은 작동합니다. 타이머가 아닌 버전의 코드에서 무엇이 잘못되었는지 궁금합니다.


1
@adardesign 아니, 내가 한 적이 없어, 나는 타이머를 사용해야했다. 어떤 이유로 든 콜백은 실제로 콜백이 아닙니다. 11 개의 열과 100 개의 행이있는 테이블이 있으므로 자연스럽게 각도는 데이터 바인딩에 사용하는 것이 좋습니다. 그러나 $ ( 'table'). datatable ()처럼 간단한 jquery Datatables 플러그인도 사용해야합니다. 지시문을 사용하거나 모든 행이있는 바보 같은 json 객체를 가지고 ng-repeat를 사용하여 반복합니다. 테이블 html 요소가 렌더링 된 후에 $ (). datatable ()을 실행하여 실행할 수 없으므로 현재 트릭은 타이머입니다. $ ( 'tr'). length> 3 (b / c 머리글 / 바닥 글) 여부 확인
Nik So

2
@adardesign 그리고 네, 모든 컴파일 방법, postLink / preLink 방법을 포함하는 객체를 반환하는 컴파일 방법, 함수 (즉, 연결 기능) 만 반환하는 컴파일 방법, 연결 방법 (컴파일 방법이없는 한 컴파일 방법없이)을 시도했습니다. 링크 메소드를 리턴하는 컴파일 메소드가있는 경우 링크 함수는 무시됩니다.) 아무 것도 작동하지 않으므로 이전 $ timeout에 의존해야합니다. 콜백이 실제로 콜백처럼 작동하는 것을 발견했을 때 더 잘 작동하거나 간단하게 작동하면이 게시물을 업데이트합니다.
Nik So

답변:


215

두 번째 매개 변수 "delay"가 제공되지 않으면 기본 동작은 DOM이 렌더링을 완료 한 후 함수를 실행하는 것입니다. 따라서 setTimeout 대신 $ timeout을 사용하십시오.

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

8
문서에 설명되어 있지 않습니까?
Gaui

23
당신이 옳아 요, 내 대답은 내가 간단하게 만들려고 노력했기 때문에 약간 오도 된 것입니다. 정답은이 효과가 Angular의 결과가 아니라 브라우저의 결과라는 것입니다. $timeout(fn)궁극적으로 setTimeout(fn, 0)Javascript 실행을 중단하고 해당 Javascript 실행을 계속하기 전에 브라우저가 컨텐츠를 먼저 렌더링 할 수 있도록하는 호출 을 호출합니다 .
의회

7
브라우저를 "자바 스크립트 실행"및 "DOM 렌더링"과 같은 특정 작업을 개별적으로 큐로 간주하고 렌더링 한 후 현재 실행중인 "자바 스크립트 실행"을 큐의 뒷면으로 푸시하는 setTimeout (fn, 0) .
의회

2
@GabLeRoux yup, $ timeout은 $ scope. $ apply ()가 실행 된 후 호출하는 이점이 있습니다. _.defer ()를 사용하면 myFunction이 범위의 변수를 변경하는 경우 수동으로 호출해야합니다.
의회

2
나는 이것이 1 페이지에서 ng-repeat가 많은 요소를 렌더링하는 위치에 도움이되지 않는 시나리오를 겪고 있습니다. 그런 다음 page2로 이동 한 다음 page1로 돌아가서 높은 ng-repeat 요소를 상위로 가져 오려고 시도합니다 ... 잘못된 높이를 반환합니다. 1000ms와 같은 시간 초과를 수행하면 작동합니다.
yodalr

14

나는 똑같은 문제가 있었고 대답은 실제로 아니요라고 믿습니다. Miško의 의견그룹 내 토론을 참조하십시오 .

Angular는 DOM을 조작하는 데 필요한 모든 함수 호출이 완료되었음을 추적 할 수 있지만 이러한 함수는 DOM을 반환 한 후에도 여전히 DOM을 업데이트하는 비동기 논리를 트리거 할 수 있으므로 Angular는 이에 대해 알 수 없었습니다. 각도가주는 모든 콜백 때로는 작동하지만에 의존하는 안전하지 않을 것입니다.

우리는 setTimeout을 사용하여이를 발견 적으로 해결했습니다.

(모두가 나에게 동의하는 것은 아니라는 점을 명심하십시오. 위의 링크에 대한 의견을 읽고 자신의 생각을 확인하십시오.)


7

템플릿을 넣은 후에 실행되는 postLink라고도하는 'link'기능을 사용할 수 있습니다.

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

지시어를 만들 계획이라면이 내용을 읽으십시오. 큰 도움이됩니다 : http://docs.angularjs.org/guide/directive


앤디 안녕, 답변 주셔서 감사합니다; 링크 기능을 사용해 보았지만 코드를 작성할 때 정확하게 다시 시도해도 상관 없습니다. 지난 1.5 일 동안 그 지시문 페이지를 읽었습니다. 각도 사이트의 예제도 살펴보십시오. 지금 코드를 사용해보십시오.
Nik So

아, 나는 지금 당신이 링크를하려고했지만 당신이 잘못하고있는 것을 본다. 방금 함수를 반환하면 링크 된 것으로 가정합니다. 객체를 반환하면 키를 'link'로 반환해야합니다. 컴파일 함수에서 연결 함수를 반환 할 수도 있습니다.
앤드류 조슬린

안녕 앤디, 내 결과를 다시 얻었 어; 나는 기본적으로 여기에 당신의 대답이 무엇인지 실제로했기 때문에 내 정신을 거의 잃었습니다. 내 업데이트를 참조하십시오
Nik So

<table id = "bob"> <tbody dashboard-table = "# bob"> </ tbody> </ table> 그런 다음 링크에서 $ (attrs.dashboardTable) .dataTable ()을 수행하십시오. 올바르게 선택되어 있는지 확인하십시오. 또는 이미 시도한 것 같습니다. 링크가 작동하지 않는지 잘 모르겠습니다.
앤드류 조슬린

이것은 나를 위해 일했고, 내 요구 사항에 대한 템플릿의 dom post 렌더링 내에서 요소를 옮기고 싶었고, 링크 기능에서 그렇게했습니다.
감사

7

내 대답은 데이터 테이블과 관련이 없지만 DOM 조작 및 예를 들어 내용이 비동기 방식으로 업데이트 된 요소에 사용되는 지시문에 대한 jQuery 플러그인 초기화 문제를 해결합니다.

타임 아웃을 구현하는 대신 콘텐츠 변경 (또는 추가 외부 트리거)을 수신하는 시계를 추가 할 수 있습니다.

내 경우에는 내 내부 DOM을 생성 한 ng-repeat가 완료되면 jQuery 플러그인을 초기화하기 위해이 해결 방법을 사용했습니다. 다른 경우에는 컨트롤러에서 scope 속성이 변경된 후 DOM을 조작하는 데 사용했습니다. 여기 내가 한 방법이 있습니다 ...

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

                }
            }
        }
    }
}]);

노트 : my-directive-watch 속성에서 bool하기 위해 myContent 변수를 캐스팅하는 대신 임의의 표현을 상상할 수 있습니다.

참고 : 위 예제에서와 같이 범위를 분리하는 것은 요소 당 한 번만 수행 할 수 있습니다. 동일한 요소에 여러 지시문을 사용하여이를 수행하면 $ compile : multidir 오류가 발생합니다. https://docs.angularjs.org / error / $ compile / multidir


7

이 질문에 대답하기에 늦을 수 있습니다. 그러나 여전히 누군가 내 대답에서 혜택을 얻을 수 있습니다.

나는 비슷한 문제가 있었고 내 경우에는 지시문을 변경할 수 없으므로 라이브러리이므로 라이브러리의 코드를 변경하는 것은 좋은 습관이 아닙니다. 그래서 내가 한 일은 페이지로드를 기다리는 변수를 사용하고 html 내에서 ng-if를 사용하여 특정 요소를 렌더링하는 것입니다.

내 컨트롤러에서 :

$scope.render=false;

//this will fire after load the the page

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

내 html에서 (내 경우 html 구성 요소는 캔버스입니다)

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

3

나는 같은 문제가 있었지만 fnDrawCallback+ 행 그룹화 + $ 컴파일 된 중첩 지시문 과 함께 Angular + DataTable을 사용했습니다 . fnDrawCallback페이지 매김 렌더링을 수정하기 위해 $ timeout을 함수 에 배치했습니다 .

예를 들어, 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
  }
}

예를 들면 다음과 같습니다.

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
}

짧은 타임 아웃 지연조차도 Angular가 컴파일 된 Angular 지시문을 렌더링 할 수있을 정도로 충분했습니다.


궁금한 점이 있습니다. 열이 많은 테이블이 있습니까? dataTable () 호출을 질식시키지 않도록 성가신 밀리 초 (> 100)가 필요하다는 것을 알았 기 때문에
Nik So

2 행에서 150 행 이상의 결과 세트에 대한 DataTable 페이지 탐색 시 문제가 발생 했음을 발견했습니다 . 따라서 아니요. 테이블 크기가 문제라고 생각하지 않지만 DataTable은 렌더링 시간을 추가하여 밀리 초를 씹을 수 있습니다. 최소의 AngularJS 통합으로 DataTable에서 행 그룹화를 작동시키는 데 중점을 두었습니다.
JJ Zabkar

2

나를 위해 일한 솔루션 중 어느 것도 시간 초과를 사용하지 않습니다. postLink 중에 동적으로 생성 된 템플릿을 사용했기 때문입니다.

그러나 타임 아웃이 각도 렌더링 엔진 이후에 발생하는 함수를 브라우저의 대기열에 이미 추가했기 때문에 타임 아웃이 '0'일 수 있습니다.

이것을 참조하십시오 : http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering


0

다음은 얕은 렌더링 후에 동작을 프로그래밍하라는 지시문입니다. 얕은 의미는 요소를 렌더링 한 후에 평가 하고 내용이 렌더링 될 때 관련이 없음을 의미합니다 . 따라서 렌더링 후 작업을 수행하는 일부 하위 요소가 필요한 경우 여기에서 사용하는 것이 좋습니다.

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

그럼 당신은 할 수 있습니다 :

<div after-render></div>

또는 다음과 같은 유용한 표현으로 :

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


실제로 콘텐츠가 렌더링 된 후에는 아닙니다. 이 시점에서 <div after-render> {{blah}} </ div> 요소 안에 표현식이 있으면 표현식은 아직 평가되지 않습니다. div의 내용은 여전히 ​​링크 함수 안에 {{blah}}입니다. 따라서 기술적으로 콘텐츠를 렌더링하기 전에 이벤트를 시작합니다.
Edward Olamisan 2016 년

이것은 렌더 작업 후 얕음, 나는 그것이 깊은 것이라고 주장한 적이 없습니다
Sebastian Sastre

0

나는 이것을 다음 지시문과 함께 사용했다.

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

그리고 HTML에서 :

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

위의 방법으로 문제가 해결되지 않으면

1) 'datatableSetup'은 'datatable-setup'과 같습니다. Angular는 형식을 낙타 케이스로 변경합니다.

2) 지시문 전에 앱이 정의되어 있는지 확인하십시오. 예를 들어 간단한 앱 정의 및 지시문.

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

0

로드 순서를 예상 할 수 없다는 사실에 따라 간단한 솔루션을 사용할 수 있습니다.

지시어- '지시자의 사용자'관계를 살펴 보자. 일반적으로 지시문 사용자는 지시문에 일부 데이터를 제공하거나 지시문이 제공하는 일부 기능 (기능)을 사용합니다. 반면에 지시문은 일부 변수가 해당 범위에서 정의 될 것으로 예상합니다.

모든 플레이어가 해당 액션을 실행하기 전에 모든 액션 요구 사항을 충족시킬 수 있다면 모든 것이 잘되어야합니다.

그리고 이제 지시어 :

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

        };
})

이제 지시문 html의 사용자

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

지시문을 사용하는 구성 요소의 컨트롤러 어딘가에 :

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

그게 다야. 오버 헤드가 많지만 $ timeout을 잃을 수 있습니다. 또한 지시문이 인스턴스화 될 때 존재하는 제어 변수에 의존하기 때문에 지시문을 사용하는 구성 요소가 지시문보다 먼저 인스턴스화되는 것으로 가정합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.