AngularJS : AngularJS가 템플릿을 렌더링 한 후 추가 코드를 실행하는 방법은 무엇입니까?


182

DOM에 Angular 템플릿이 있습니다. 컨트롤러가 서비스에서 새 데이터를 가져 오면 $ scope에서 모델을 업데이트하고 템플릿을 다시 렌더링합니다. 지금까지는 모두 좋았습니다.

문제는 템플릿을 다시 렌더링하고 DOM (이 경우 jQuery 플러그인)에 넣은 후에 추가 작업을 수행해야한다는 것입니다.

AfterRender와 같은 청취 이벤트가 있어야하는 것처럼 보이지만 그러한 것을 찾을 수 없습니다. 지시어가 갈 수있는 방법 일지 모르지만 너무 일찍 발사되는 것처럼 보였습니다.

여기 내 문제를 간략히 설명하는 jsFiddle이 있습니다. Fiddle-AngularIssue

== 업데이트 ==

유용한 의견을 바탕으로 DOM 조작을 처리하기 위해 지시문으로 전환했으며 지시문 안에 $ watch 모델을 구현했습니다. 그러나 여전히 동일한 기본 문제가 있습니다. 템플릿이 컴파일되어 DOM에 삽입되기 전에 $ watch 이벤트 내부의 코드가 실행되므로 jquery 플러그인은 항상 빈 테이블을 평가합니다.

흥미롭게도 비동기 호출을 제거하면 모든 것이 잘 작동하므로 올바른 방향으로 나아가는 단계입니다.

다음은 이러한 변경 사항을 반영하기 위해 업데이트 된 Fiddle입니다. http://jsfiddle.net/uNREn/12/


이것은 내가 가진 질문과 유사합니다. stackoverflow.com/questions/11444494/... . 거기에 뭔가 도움이 될 수 있습니다.
dnc253

답변:


39

이 게시물은 오래되었지만 코드를 다음과 같이 변경합니다.

scope.$watch("assignments", function (value) {//I change here
  var val = value || null;            
  if (val)
    element.dataTable({"bDestroy": true});
  });
}

jsfiddle을 참조하십시오 .

도움이 되길 바랍니다


고마워, 그것은 매우 도움이됩니다. 이것은 컨트롤러에서 dom 조작을 필요로하지 않기 때문에 나에게 가장 적합한 솔루션이며 데이터가 진정한 비동기 서비스 응답에서 업데이트 될 때도 작동합니다 (컨트롤러에서 $ evalAsync를 사용할 필요가 없습니다).
gorebash

9
더 이상 사용되지 않습니까? 이것을 시도하면 실제로 DOM이 렌더링 되기 직전에 호출 됩니다. 그림자 돔 등에 의존합니까?
Shayne

나는 Angular를 처음 접했고 특정 사례 로이 답변을 구현하는 방법을 볼 수 없습니다. 나는 다양한 답변을보고 추측했지만 작동하지 않습니다. 'watch'기능이 무엇을보고 있는지 잘 모르겠습니다. 우리의 앵귤러 앱에는 다양한 언어 지시문이 있으며 페이지마다 다를 수 있으므로 '보아야 할 것'을 어떻게 알 수 있습니까? 페이지 렌더링이 완료되면 일부 코드를 실행하는 간단한 방법이 있습니까?
moosefetcher

63

먼저, 렌더링을 망칠 적절한 장소는 지시어입니다. 내 조언은 jQuery 플러그인을 조작하는 DOM을 이와 같은 지시문으로 감싸는 것입니다.

나는 같은 문제가 있었고이 발췌 문장을 생각해 냈습니다. 그것은 사용 $watch$evalAsync지시가 좋아 후 코드 실행을 보장하기 위해 ng-repeat해결과 같은 템플릿 된 {{ value }}렌더링되었다.

app.directive('name', function() {
    return {
        link: function($scope, element, attrs) {
            // Trigger when number of children changes,
            // including by directives like ng-repeat
            var watch = $scope.$watch(function() {
                return element.children().length;
            }, function() {
                // Wait for templates to render
                $scope.$evalAsync(function() {
                    // Finally, directives are evaluated
                    // and templates are renderer here
                    var children = element.children();
                    console.log(children);
                });
            });
        },
    };
});

이것이 약간의 투쟁을 예방하는 데 도움이되기를 바랍니다.


이것은 명백 할 수도 있지만 이것이 효과가 없다면 링크 기능 / 컨트롤러에서 비동기 작업을 확인하십시오. 나는 $ evalAsync가 그것들을 고려할 수 있다고 생각하지 않습니다.
LOAS

link함수 와을 결합 할 수도 있습니다 templateUrl.
Wtower

35

비동기 작업을 원한다면 Misko의 조언에 따라 $ timeout () 대신 작동하지 않습니다.

$timeout(function () { $scope.assignmentsLoaded(data); }, 1000);

$ evalAsync () 사용하십시오 (작동합니다)

$scope.$evalAsync(function() { $scope.assignmentsLoaded(data); } );

바이올린 . 또한 $ scope.assignments를 수정하여 데이터 / 모델 변경을 시뮬레이션하는 "데이터 행 제거"링크를 추가하여 데이터 변경이 작동 함을 보여줍니다.

개념적 개요 페이지 의 런타임 섹션에서는 현재 스택 프레임 외부에서 발생해야하지만 브라우저가 렌더링되기 전에 evalAsync를 사용해야한다고 설명합니다. (여기서 ... "현재 스택 프레임"에는 아마도 Angular DOM 업데이트가 포함되어있을 것입니다.) 브라우저가 렌더링 된 후에 무언가가 필요하면 $ timeout을 사용하십시오.

그러나 이미 알았 듯이 여기서 비동기 작업이 필요하지 않다고 생각합니다.


4
$scope.$evalAsync($scope.assignmentsLoaded(data));이해가되지 않습니다. 의 인수 $evalAsync()는 함수 여야합니다. 귀하의 예에서는 나중에 실행하기 위해 함수를 등록해야 할 때 함수를 호출합니다.
hgoebl

@hgoebl에서 $ evalAsync 의 인수 는 함수 또는 표현식 일 수 있습니다. 이 경우 표현식을 사용했습니다. 그러나 당신 말이 맞아, 표현은 즉시 실행됩니다. 나중에 실행하기 위해 함수에 래핑하도록 답변을 수정했습니다. 찾아 주셔서 감사합니다.
Mark Rajcok

26

가장 간단한 (저렴하고 쾌활한) 솔루션은 마지막으로 렌더링 된 요소의 끝에 ng-show = "someFunctionThatAlwaysReturnsZeroOrNothing ()"으로 빈 범위를 추가하는 것입니다. 이 기능은 span 요소가 표시되어야하는지 확인할 때 실행됩니다. 이 함수에서 다른 코드를 실행하십시오.

나는 이것이 가장 우아한 방법이 아니라는 것을 알고 있지만 그것은 나를 위해 작동합니다 ...

애니메이션이 시작될 때 로딩 표시기를 제거 해야하는 부분이 약간 바뀌었지만 비슷한 상황이 발생했습니다. 모바일 장치에서 각도가 애니메이션보다 표시가 훨씬 빠르게 초기화되었으며 로딩 표시기가 ng-cloak을 사용하는 것만으로는 충분하지 않았습니다 실제 데이터가 표시되기 전에 제거되었습니다. 이 경우 방금 리턴 0 함수를 첫 번째 렌더링 된 요소에 추가했으며이 함수에서로드 표시기를 숨기는 var를 뒤집 었습니다. (물론이 기능으로 트리거되는 로딩 표시기에 ng-hide를 추가했습니다.


3
이것은 확실한 해킹이지만 정확히 내가 필요한 것입니다. 렌더링 후 코드가 정확하게 실행되도록 (또는 매우 정확하게) 강제 실행했습니다. 이상적인 세상에서 나는 이것을하지 않을 것이지만 여기에 우리가 있습니다 ...
Andrew

$anchorScrollDOM 렌더링 후에 내장 서비스를 호출해야했지만 트릭을 수행하는 것은 아무것도 없었습니다. 해 키시지만 작동합니다.
Pat Migliaccio

ng-init가 더 나은 대안입니다
Flying Gambit

1
ng-init가 항상 작동하지 않을 수 있습니다. 예를 들어, dom에 많은 이미지를 추가하는 ng-repeat가 있습니다. 각 이미지가 ng-repeat에 추가 된 후 함수를 호출하려면 ng-show를 사용해야합니다.
Michael Bremerkamp


4

마지막으로 해결책을 찾았습니다. 내 컬렉션을 업데이트하기 위해 REST 서비스를 사용하고있었습니다. 데이터 테이블을 변환하기 위해 jquery는 다음 코드입니다.

$scope.$watchCollection( 'conferences', function( old, nuew ) {
        if( old === nuew ) return;
        $( '#dataTablex' ).dataTable().fnDestroy();
        $timeout(function () {
                $( '#dataTablex' ).dataTable();
        });
    });

3

나는 이것을 아주 자주해야했습니다. 지시어가 있으며 모델 항목이 DOM에 완전히로드 된 후 일부 jquery 작업을 수행해야합니다. 그래서 나는 내 논리를 링크에 넣었다 : 지시자의 함수와 코드를 setTimeout (function () {.....}, 1); setTimout은 DOM이로드 된 후 시작되며 1 밀리 초는 코드가 실행되기 전에 DOM이로드 된 후 가장 짧은 시간입니다. 이것은 나를 위해 작동하는 것 같지만 템플릿이로드되면 angular가 이벤트를 발생시켜 해당 템플릿에서 사용하는 지시문이 jquery 작업을 수행하고 DOM 요소에 액세스 할 수 있기를 바랍니다. 도움이 되었기를 바랍니다.



2

$ scope. $ evalAsync () 또는 $ timeout (fn, 0) 어느 것도 안정적으로 작동하지 않았습니다.

나는 둘을 결합해야했다. 지침을 작성하고 올바른 측정을 위해 기본값보다 우선 순위를 높였습니다. 여기에 지시문이 있습니다 (참고로 ngInject를 사용하여 종속성을 주입합니다).

app.directive('postrenderAction', postrenderAction);

/* @ngInject */
function postrenderAction($timeout) {
    // ### Directive Interface
    // Defines base properties for the directive.
    var directive = {
        restrict: 'A',
        priority: 101,
        link: link
    };
    return directive;

    // ### Link Function
    // Provides functionality for the directive during the DOM building/data binding stage.
    function link(scope, element, attrs) {
        $timeout(function() {
            scope.$evalAsync(attrs.postrenderAction);
        }, 0);
    }
}

지시문을 호출하려면 다음을 수행하십시오.

<div postrender-action="functionToRun()"></div>

ng-repeat가 실행 된 후에 전화를 걸려면 ng-repeat 및 ng-if = "$ last"에 빈 범위를 추가했습니다.

<li ng-repeat="item in list">
    <!-- Do stuff with list -->
    ...

    <!-- Fire function after the last element is rendered -->
    <span ng-if="$last" postrender-action="$ctrl.postRender()"></span>
</li>

1

서비스를 업데이트하고 새보기 (페이지)로 리디렉션 한 다음 서비스가 업데이트되기 전에 지시문이로드되는 일부 시나리오에서는 $ watch 또는 $ timeout이 실패하면 $ rootScope. $ broadcast를 사용할 수 있습니다.

전망

<service-history log="log" data-ng-repeat="log in requiedData"></service-history>

제어 장치

app.controller("MyController",['$scope','$rootScope', function($scope, $rootScope) {

   $scope.$on('$viewContentLoaded', function () {
       SomeSerive.getHistory().then(function(data) {
           $scope.requiedData = data;
           $rootScope.$broadcast("history-updation");
       });
  });

}]);

지령

app.directive("serviceHistory", function() {
    return {
        restrict: 'E',
        replace: true,
        scope: {
           log: '='
        },
        link: function($scope, element, attrs) {
            function updateHistory() {
               if(log) {
                   //do something
               }
            }
            $rootScope.$on("history-updation", updateHistory);
        }
   };
});

1

나는 매우 간단한 해결책을 찾았다. 그것이 올바른 방법인지 확실하지 않지만 실용적인 의미로 작동합니다. 우리가 렌더링하고 싶은 것을 직접 보자. 예를 들어 ng-repeats 를 포함하는 지시문 에서 단락의 전체 텍스트 길이 또는 다른 HTML이 있는지 확인합니다. 지시문은 다음과 같습니다.

.directive('myDirective', [function () {
    'use strict';
    return {

        link: function (scope, element, attrs) {
            scope.$watch(function(){
               var whole_p_length = 0;
               var ps = element.find('p');
                for (var i=0;i<ps.length;i++){
                    if (ps[i].innerHTML == undefined){
                        continue
                    }
                    whole_p_length+= ps[i].innerHTML.length;
                }
                //it could be this too:  whole_p_length = element[0].innerHTML.length; but my test showed that the above method is a bit faster
                console.log(whole_p_length);
                return whole_p_length;
            }, function (value) {   
                //Code you want to be run after rendering changes
            });
        }
}]);

참고 한 후 코드가 실제로 실행되는 렌더링이 변경 이 아니라 완전한 렌더링. 그러나 대부분의 경우 렌더링 변경이 발생할 때마다 상황을 처리 할 수 ​​있다고 생각합니다. 또한 렌더링이 완료된 후 p코드를 한 번만 실행하려는 경우이 길이 (또는 다른 측정 값)를 모델과 비교할 수 있습니다 . 이에 대한 의견이나 의견에 감사드립니다.


0

angular-ui utils 의 'jQuery Passthrough'모듈을 사용할 수 있습니다 . 웹 서비스에서 비동기를 검색하고 ng-repeat로 렌더링하는 일부 이미지에 jQuery touch carousel 플러그인을 성공적으로 바인딩했습니다.

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