각도 지시문-컴파일, 컨트롤러, 사전 링크 및 사후 링크 사용시기 및 방법 [폐쇄]


451

Angular 지시문을 작성할 때 다음 함수 중 하나를 사용하여 지시문이 선언 된 요소의 DOM 동작, 내용 및 모양을 조작 할 수 있습니다.

  • 엮다
  • 제어 장치
  • 사전 연결
  • 사후 링크

어떤 기능을 사용해야하는지 혼동이있는 것 같습니다. 이 질문은 다음과 같습니다.

지시문의 기초

기능 성격,해야 할 것과하지 말아야 할 것

관련 질문 :


27
뭐야?
haimlit

2
@Ian 참조 : 연산자 과부하 . 본질적으로 이것은 커뮤니티 위키를위한 것입니다. 관련 질문에 대한 답변 중 너무 많은 부분이 부분적인 것으로 전체 그림을 제공하지는 않습니다.
Izhaki

8
이것은 훌륭한 내용이지만 여기에있는 모든 내용은 Q & A 형식으로 유지해야합니다. 아마도 이것을 여러 개의 개별 질문으로 나누고 태그 위키에서 질문에 연결하고 싶습니까?
Flexo

57
이 글은 주제가 아닌 블로그 형식이지만 Angular 지시문에 대한 자세한 설명을 제공하는 데 가장 유용했습니다. 이 게시물 관리자를 삭제하지 마십시오!
Exegesis

12
솔직히, 나는 원래 문서를 귀찮게하지 않습니다. 스택 오버 플로우 게시물이나 블로그는 일반적으로 원래 문서를 이해하려고 머리카락을 찢는 15-30 분에 비해 몇 초 만에 나에게 갈 수 있습니다.
David

답변:


168

지시문 함수는 어떤 순서로 실행됩니까?

단일 지시문

다음 plunk를 기반으로 다음 HTML 마크 업을 고려하십시오.

<body>
    <div log='some-div'></div>
</body>

다음 지시문 선언으로 :

myApp.directive('log', function() {

    return {
        controller: function( $scope, $element, $attrs, $transclude ) {
            console.log( $attrs.log + ' (controller)' );
        },
        compile: function compile( tElement, tAttributes ) {
            console.log( tAttributes.log + ' (compile)'  );
            return {
                pre: function preLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (pre-link)'  );
                },
                post: function postLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (post-link)'  );
                }
            };
         }
     };  

});

콘솔 출력은 다음과 같습니다.

some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)

우리는 볼 수 compile첫째, 다음 실행 controller, 다음 pre-link과 마지막이다 post-link.

중첩 지시문

참고 : 다음은 링크 기능에서 하위를 렌더링하는 지시문에는 적용되지 않습니다. 꽤 많은 Angular 지시어가 그렇게합니다 (ngIf, ngRepeat 또는을 사용하는 지시어 transclude). 이러한 지시문은 기본적으로 자식 지시문 이 호출 되기 전에link 함수 가 호출됩니다.compile

원래 HTML 마크 업은 종종 고유 한 지시문이있는 중첩 요소로 만들어집니다. 다음 마크 업에서와 같이 ( plunk 참조 ) :

<body>
    <div log='parent'>
        <div log='..first-child'></div>
        <div log='..second-child'></div>
    </div>
</body>

콘솔 출력은 다음과 같습니다.

// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)

// The link phase   
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)

여기서 컴파일 단계와 링크 단계 라는 두 단계를 구분할 수 있습니다 .

컴파일 단계

DOM이로드되면 Angular는 컴파일 단계를 시작합니다. 컴파일 단계에서 마크 업 하향식을 통과하고 compile모든 지시문을 호출 합니다. 그래픽으로 다음과 같이 표현할 수 있습니다.

어린이를위한 컴파일 루프를 보여주는 이미지

이 단계에서 컴파일 함수가 얻는 템플릿은 소스 템플릿 (인스턴스 템플릿이 아님)이라는 것을 언급하는 것이 중요합니다.

링크 단계

DOM 인스턴스는 종종 소스 템플릿이 DOM으로 렌더링 된 결과 일 뿐이지 만에 의해 생성 ng-repeat되거나 즉시 도입 될 수 있습니다 .

지시문이있는 요소의 새 인스턴스가 DOM에 렌더링 될 때마다 링크 단계가 시작됩니다.

이 단계에서 Angular는 controller,를 호출 하고 pre-link자식을 반복하고 다음과 post-link같이 모든 지시문을 호출 합니다.

링크 단계 단계를 보여주는 그림


5
@lzhaki 플로우 차트가 좋아 보인다. 차트 도구 이름을 공유 하시겠습니까? :)
merlin

1
@merlin 저는 OmniGraffle을 사용했습니다 (그러나 일러스트 레이터 나 잉크 스케이프를 사용했을 수도 있습니다. 속도 이외에는 OmniGraffle이이 그림에 관한 한 다른 차트 도구보다 더 좋은 것은 없습니다).
Izhaki

2
@Anant의 plunker가 사라 졌으므로 여기에 새로운 것이 있습니다 : plnkr.co/edit/kZZks8HN0iFIY8ZaKJkA?p=preview JS 콘솔을 열어서 로그 설명을보십시오

ng-repeat가 어린이 지시문에 사용될 때 이것이 사실이 아닌 이유는 무엇입니까 ??? plunk
Luckylooke

@Luckylooke plunk는 ng-repeat 아래에 지시문이있는 자식이 없습니다. 즉, 반복되는 것은 지시문이있는 템플릿입니다. 그렇다면 ng-repeat의 링크 후에 만 ​​컴파일이 호출됩니다.
Izhaki

90

이 함수 호출 사이에 다른 일이 있습니까?

다양한 지시자 함수는 두 개의 다른 각도 함수 호출 내에서 실행된다 $compile(지향성의이 compile실행된다) 및 내부 함수 호출 nodeLinkFn(지향성의 controller, preLink그리고 postLink실행된다). 지시문 함수가 호출되기 전과 후에 각도 함수 내에서 다양한 일이 발생합니다. 아마도 가장 주목할만한 것은 자식 재귀입니다. 다음 단순화 된 그림은 컴파일 및 링크 단계 내의 주요 단계를 보여줍니다.

Angular 컴파일 및 링크 단계를 보여주는 그림

이러한 단계를 보여주기 위해 다음 HTML 마크 업을 사용하겠습니다.

<div ng-repeat="i in [0,1,2]">
    <my-element>
        <div>Inner content</div>
    </my-element>
</div>

다음 지시문으로 :

myApp.directive( 'myElement', function() {
    return {
        restrict:   'EA',
        transclude: true,
        template:   '<div>{{label}}<div ng-transclude></div></div>'
    }
});

엮다

compileAPI의 모습이 너무 좋아 :

compile: function compile( tElement, tAttributes ) { ... }

종종 t제공되는 요소와 속성을 나타 내기 위해 매개 변수가 접두사로 제공되어 인스턴스의 속성이 아닌 소스 템플릿의 속성과 속성이 제공됩니다.

compile변환 된 컨텐츠 에 대한 호출 (있는 경우)이 제거되고 템플리트가 마크 업에 적용됩니다. 따라서 compile함수에 제공된 요소 는 다음과 같습니다.

<my-element>
    <div>
        "{{label}}"
        <div ng-transclude></div>
    </div>
</my-element>

이 시점에서 변환 된 컨텐츠는 다시 삽입되지 않습니다.

지시문을 호출 한 후 .compileAngular는 지시문에 의해 도입되었을 수있는 요소 (예 : 템플릿 요소)를 포함하여 모든 하위 요소를 순회합니다.

인스턴스 생성

이 경우 위의 소스 템플릿 인스턴스가 3 개씩 생성됩니다 ng-repeat. 따라서 다음 시퀀스는 인스턴스 당 한 번씩 세 번 실행됩니다.

제어 장치

controllerAPI에는 다음 이 포함됩니다.

controller: function( $scope, $element, $attrs, $transclude ) { ... }

링크 단계로 들어가면,을 통해 리턴 된 링크 기능 $compile에 범위가 제공됩니다.

먼저 링크 함수는 요청 된 경우 하위 범위 ( scope: true) 또는 격리 된 범위 ( scope: {...})를 만듭니다.

그런 다음 인스턴스 요소의 범위와 함께 컨트롤러가 실행됩니다.

사전 연결

pre-linkAPI의 모습이 너무 좋아 :

function preLink( scope, element, attributes, controller ) { ... }

지시문 호출 .controller.preLink함수 사이에는 사실상 아무 것도 발생하지 않습니다 . Angular는 여전히 각각의 사용법에 대한 권장 사항을 제공합니다.

.preLink호출 후 링크 함수는 각 하위 요소를 순회합니다. 올바른 링크 함수를 호출하고 현재 범위 (하위 요소의 상위 범위 역할을 함)에 연결합니다.

포스트 링크

post-linkAPI는 pre-link함수 의 API와 유사 합니다.

function postLink( scope, element, attributes, controller ) { ... }

지시어 .postLink함수가 호출되면 모든 자식 .postLink함수를 포함하여 모든 자식 요소의 링크 프로세스가 완료 되었음을 알 수 있습니다.

이것은 시간 .postLink이 지남 에 따라 아이들이 '살아있다'는 것을 의미합니다 . 여기에는 다음이 포함됩니다.

  • 데이터 바인딩
  • transclusion이 적용됨
  • 연결된 범위

따라서이 단계의 템플릿은 다음과 같습니다.

<my-element>
    <div class="ng-binding">
        "{{label}}"
        <div ng-transclude>                
            <div class="ng-scope">Inner content</div>
        </div>
    </div>
</my-element>

3
이 그림을 어떻게 만들었습니까?
Royi Namir

6
@RoyiNamir Omnigraffle.
Izhaki

43

다양한 함수를 선언하는 방법?

컴파일, 컨트롤러, 프리 링크 및 포스트 링크

하나가 네 가지 기능을 모두 사용하는 경우 지시문은 다음 형식을 따릅니다.

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.
            return {
                pre: function preLink( scope, element, attributes, controller, transcludeFn ) {
                    // Pre-link code goes here
                },
                post: function postLink( scope, element, attributes, controller, transcludeFn ) {
                    // Post-link code goes here
                }
            };
        }
    };  
});

compile은 pre-link 함수와 post-link 함수를 모두 포함하는 객체를 반환합니다. Angular lingo에서는 컴파일 함수가 템플릿 함수를 반환한다고 말합니다 .

컴파일, 컨트롤러 및 사후 링크

경우 pre-link필요하지 않습니다, 컴파일 기능은 단순히 대신 정의 오브젝트의 후 링크 기능을 반환과 같이 할 수 있습니다 :

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.
            return function postLink( scope, element, attributes, controller, transcludeFn ) {
                    // Post-link code goes here                 
            };
        }
    };  
});

때로는 compile(post) link메소드가 정의 된 후 메소드 를 추가하려고합니다 . 이를 위해 다음을 사용할 수 있습니다.

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.

            return this.link;
        },
        link: function( scope, element, attributes, controller, transcludeFn ) {
            // Post-link code goes here
        }

    };  
});

컨트롤러 및 사후 링크

컴파일 함수가 필요하지 않은 경우 선언을 모두 건너 뛰고 link지시문 구성 오브젝트의 특성 아래에서 사후 링크 함수를 제공 할 수 있습니다 .

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-link code goes here                 
        },          
    };  
});

컨트롤러 없음

위의 예에서, controller필요하지 않은 경우 단순히 기능을 제거 할 수 있습니다 . 예를 들어 post-link기능 만 필요한 경우 다음을 사용할 수 있습니다.

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-link code goes here                 
        },          
    };  
});

31

소스 템플릿인스턴스 템플릿 의 차이점은 무엇입니까 ?

Angular가 DOM 조작을 허용한다는 사실은 컴파일 프로세스에 대한 입력 마크 업이 때때로 출력과 다름을 의미합니다. 특히, 일부 입력 마크 업은 ng-repeatDOM에 렌더링되기 전에 (와 같은 ) 몇 번 복제 될 수 있습니다 .

각도 용어는 약간 일치하지 않지만 여전히 두 가지 유형의 마크 업을 구분합니다.

  • 소스 템플릿 -필요한 경우 복제 할 마크 업입니다. 복제 된 경우이 마크 업은 DOM에 렌더링되지 않습니다.
  • 인스턴스 템플릿 -DOM에 렌더링 할 실제 마크 업입니다. 복제가 포함 된 경우 각 인스턴스는 복제본이됩니다.

다음 마크 업이이를 보여줍니다.

<div ng-repeat="i in [0,1,2]">
    <my-directive>{{i}}</my-directive>
</div>

소스 HTML은

    <my-directive>{{i}}</my-directive>

소스 템플릿으로 사용됩니다.

그러나 ng-repeat지시문에 래핑 되면이 소스 템플릿이 복제됩니다 (이 경우 3 번). 이 복제본은 인스턴스 템플릿이며 각각 복제본에 표시되며 관련 범위에 바인딩됩니다.


23

컴파일 기능

compileAngular 부트 스트랩시 각 지시문의 함수는 한 번만 호출됩니다.

공식적으로 이것은 범위 또는 데이터 바인딩과 관련이없는 템플릿 조작을 수행 할 수있는 곳입니다.

주로 최적화 목적으로 수행됩니다. 다음 마크 업을 고려하십시오.

<tr ng-repeat="raw in raws">
    <my-raw></my-raw>
</tr>

<my-raw>지시어는 DOM 마크 업의 특정 집합을 렌더링합니다. 따라서 다음 중 하나를 수행 할 수 있습니다.

  • 허용 ng-repeat소스 템플릿 (중복 <my-raw>), 다음 (외부 각 인스턴스 템플릿의 마크 업 수정 compile기능).
  • compile함수 에서 원하는 마크 업을 포함하도록 소스 템플릿을 수정 한 다음 ng-repeat복제하십시오.

raws컬렉션에 1000 개의 항목이 있으면 후자의 옵션이 이전 옵션보다 빠를 수 있습니다.

하다:

  • 마크 업을 조작하여 인스턴스 (클론)에 대한 템플릿 역할을합니다.

하지 마라

  • 이벤트 핸들러를 첨부하십시오.
  • 자식 요소를 검사하십시오.
  • 속성에 대한 관찰을 설정하십시오.
  • 스코프에서 시계를 설정하십시오.

20

컨트롤러 기능

각 지시문의 controller함수는 새로운 관련 요소가 인스턴스화 될 때마다 호출됩니다.

공식적으로 controller기능은 다음과 같습니다.

  • 컨트롤러간에 공유 될 수있는 컨트롤러 로직 (방법)을 정의합니다.
  • 범위 변수를 시작합니다.

또한 지시문에 격리 된 범위가 포함 된 경우 부모 범위에서 상속 된 모든 속성을 아직 사용할 수 없다는 점을 기억해야합니다.

하다:

  • 컨트롤러 로직 정의
  • 범위 변수 시작

하지 마라:

  • 자식 요소를 검사합니다 (아직 렌더링되지 않거나 범위에 바인딩 된 경우 등).

지시문 내의 Controller가 스코프를 초기화하기에 좋은 곳이라고 언급하게되어 기쁩니다. 나는 그것을 발견하는 데 어려움을 겪었다.
jsbisht

1
컨트롤러는 "스코프를 시작"하지 않고 이미 독립적으로 시작된 범위에만 액세스합니다.
Dmitri Zaitsev

@DmitriZaitsev 세부 사항에주의를 기울입니다. 텍스트를 수정했습니다.
Izhaki

19

사후 링크 기능

post-link바인딩, 트랜스 클루 전 등 - 함수가 호출 될 때, 이전의 모든 단계가 자리를 차지하게

일반적으로 렌더링 된 DOM을 추가로 조작 할 수있는 곳입니다.

하다:

  • DOM (렌더링 및 인스턴스화) 요소를 조작합니다.
  • 이벤트 핸들러를 첨부하십시오.
  • 자식 요소를 검사하십시오.
  • 속성에 대한 관찰을 설정하십시오.
  • 스코프에서 시계를 설정하십시오.

9
누구나 링크 기능 (사전 링크 또는 사후 링크없이)을 사용하는 경우 사후 링크와 동등한 기능을하는 것이 좋습니다.
Asaf David

15

프리 링크 기능

각 지시문의 pre-link함수는 새로운 관련 요소가 인스턴스화 될 때마다 호출됩니다.

컴파일 순서 섹션에서 이전에 본 것처럼 pre-link함수는 parent-then-child post-link라고하고 함수는이라고 child-then-parent합니다.

pre-link기능은 거의 사용되지 않지만 특별한 시나리오에서는 유용 할 수 있습니다. 아이 컨트롤러 상위 컨트롤러와 함께 자신을 등록하지만, 예를 들어, 등록은에 있어야한다 parent-then-child(패션 ngModelController이런 식으로 일을한다).

하지 마라:

  • 자식 요소를 검사합니다 (아직 렌더링되지 않거나 범위에 바인딩 된 경우 등).
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.