ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [AngularJS] 7-1. 지시자(Directive) 기본 - AngularJS 강좌
    Web/AngularJS 2016. 4. 4. 15:57

    지시자(Directive)

     HTML 문서는 여러 태그로 이루어지며, 이 태그들은 DOM으로서 계층구조입니다. 지금까지는 jQuery나 자바스크립트를 이용해서 DOM을 제어했습니다.


     AJS에서는 이러한 기존 HTML에서 제공하지 않는 기능을 확장하는 방식을 지시자로서 제공합니다.


     기존 방식은 특정 DOM에 id 속성을 부여하거나 어느 DOM 아래에 있는 특정 class를 찾거나 하는 방식으로 자바스크립트에서 DOM을 선택한 후 제이쿼리 플러그인을 적용하거나 새로운 DOM을 직접 만들어 삽입하는 방식을 취했습니다.


     AJS에서는 해당 DOM과 연결된 하나의 함수를 만들고 이 함수가 DOM을 조작하여 새로운 기능을 추가하는 등의 행위를 할 수 있습니다. 이 함수가 특정 DOM과 연결되는 지시자 함수입니다. 지시자 함수는 연결된 특정 DOM에 $scope를 연결하거나 연결된 DOM을 조작하여 특정 행위를 정의할 수 있습니다.




     1. HTML에서 지시자를 사용하는 방법

      지시자의 이름은 camel case로 작성합니다. 


      정의된 지시자를 HTML에서 호출하는 방법은 크게 4 가지 방법이 있습니다.


      a. 요소의 속성을 이용한 호출


    1
    <span my-directive="expression"></span>
    cs



      b. 요소의 클래스를 이용한 호출

     

    1
    <span class="my-directive: expression;"></span>
    cs



      c. 요소 이름을 이용한 호출


    1
    <my-directive></mydirective>
    cs



      d. 코멘트를 이용한 호출


    1
    2
    <!-- directive: my-directive expression -->
     
    cs




      그러면 AJS에서 제공하는 지시자를 다양한 방법으로 호출하는 간단한 예제 코드를 살펴보도록 합시다.



     * ajsdirective.html


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <!doctype html>
    <html ng-app="simpleApp">
        <head>
            <meta charset="UTF-8"/>
            <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
            <script>
                angular.module('simpleApp', []).
                    controller('mainCtrl'function($scope) {
                        $scope.getStyle = function() {
                            return { "color" : "red" };
                        };
                    });
            </script>    
        </head>
        <body ng-controller="mainCtrl">
            <p ng:style="getStyle()"> Hello </p>
            <p class="ng_style: getStyle();"> Palpit </p>
            <ng-form name="groupForm">
                <form name="formA"><input type="text"></form>
                <form name="formB"><input type="text"></form>
            </ng-form>
        </body>
    </html>
    cs







     2. AJS가 제공하는 지시자

      AJS에는 HTML 확장하는 여러 내장 지시자를 제공합니다. 내장된 지시자는 AJS 버전마다 다릅니다.


      이 링크를 통해서 지시자 목록을 확인하시길 바랍니다: https://docs.angularjs.org/api


      위 링크의 왼쪽 사이드바를 보면 ng 모듈 안에 지시자 목록을 확인할 수 있습니다. 목록에서 지시자를 클릭하면 사용법과 예제를 보실 수 있습니다.





     3. 사용자 정의 지시자

      AJS의 가장 큰 매력 중 하나는 AJS 지시자를 이용해 웹 UI 컴포넌트를 만들 수 있는 메카니즘을 제공한다는 것입니다. 직접 만든 컴포넌트는 양방향 데이터 바인딩을 제공할 수도 있고 도메인 특화된 HTML 태그를 구성할 수도 있습니다. 


      3-1. 간단한 지시자 정의

       다음 예제는 <div hello name="angularJS"></div> 로 작성하면 화면에 name 속성의 값을 대상으로 바인딩된 인사말을 보여주는 지시자 예제입니다.



     * ajsdirect2.html


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!doctype html>
    <html ng-app="sample">
        <head>
            <meta charset="UTF-8"/>
            <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
            <script>
                angular.module('sample', []).
                    directive('hello'function() {
                        return function(scope, iElement, iAttrs, controller) {
                            iElement.html("<h1>Hello " + iAttrs.name + "</h1>")
                        };
                    });
            </script>    
        </head>
        <body>
            <div hello name="angularJS"></div>
        </body>
    </html>
    cs









       위 예제 코드에서 directive 메서드를 사용한 코드를 봅시다. directive 메서드는 첫 번째 인자로 지시자의 이름을 요구합니다. 다음으로 지시자 설정 함수를 줄 수 있는데, 함수에서 다른 서비스의 주입을 받고 싶을 때는 서비스 이름으로 인자를 정의하면 됩니다.


       directive 메서드에서 두 번째 인자인 지시자 설정 함수는 함수나 객체를 반환해야 합니다. 위 예제 코드에서는 함수를 반환했는데 이 함수는 링크 함수입니다. 


       링크 함수는 해당 지시자가 적용된 DOM에 연결된 함수를 의미합니다. 이 연결 함수에서는 순서대로 scope 객체, 요소 객체, 속성 객체, 컨트롤러 객체가 인자로 주어집니다.


      - scope 객체: scope 객체를 통해 데이터 변경을 감지하거나 외부 컨트롤러와 데이터 바인딩할 때 사용


      - 요소 객체: AJS는 jQuery와 뛰어난 호환성을 제공하지만 jQuery 없이도 사용할 수 있게 jQuery 라이트 버전을 제공하고 있습니다. jQuery를 해당 페이지에 사용하면 연결된 DOM을 감싸고 있는 jQuery 객체가 반환되고 그렇지 않으면 AJS가 구현한 jQuery 라이트 객체를 반환합니다. 해당 객체에는 html(), append(), css() 등 jQuery에서 제공하는 DOM을 제어할 수 있는 다양한 메서드가 있습니다.


      - 속성 객체: 연결된 요소에서 속성명과 속성값을 가지는 객체입니다. 예를 들어, <tap title="hello"></tap>이라는 요소가 있을 때 해당 요소에 대한 속성 객체로서 title의 속성을 이용해 hellow라는 값을 가지고 올 수 있습니다.


      - 컨트롤러 객체: 지시자에는 해당 지시자가 사용하는 여러 요소가 공통으로 공유하는 컨트롤러를 정의할 수 있습니다. 위 예제에서는 컨트롤러를 등록하지 않았습니다. 여기서의 컨트롤러는 ng-controller로 등록하는 컨트롤러가 아니라 지시자에서 등록하는 컨트롤러임을 명심하시길 바랍니다.


      - transclude 함수: transclude 옵션을 사용할 경우 포함되는 페이지 영역에 전달할 scope에 대한 링크 함수를 정의합니다. 삽입되는 페이지 영역에 적절한 scope를 미리 연결하기 위해 사용합니다.




      3-2. 지시자 설정 객체

       지시자 설정 함수에서 반환되는 객체를 지시자 설정 객체라고 합니다. 이 설정 객체로 AJS가 지시자를 만들게 됩니다. 위 예제를 hello 지시자를 설정 객체를 이용해 다시 작성한 예제를 살펴보도록 합시다.


     * ajsdirect3.html


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <!doctype html>
    <html ng-app="sample">
        <head>
            <meta charset="UTF-8"/>
            <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
            <script>
                angular.module('sample', []).
                    directive('hello'function($log) {
                        return {
                            name0,
                            priority: 0,
                            template: '<div></div>',
                            // templateUrl: 'directive.html',
                            replace: false,
                            transclude: false,
                            restrict: 'A',
                            scope: false,
                            // require: 'ngModel',
                            controller: function($scope, $element, $attrs, $transclude) {
                            },
                            compile: function compile(tElement, tAttrs) {
                                return {
                                    pre: function preLink(scope, iElement, iAttrs, controller) {
     
                                    },
                                    post: function postLink(scope, iElement, iAttrs, controller) {
                                        $log.log("<h1>hello : " + iAttrs.name + "</h1>");
                                        iElement.html("<h1>hello : " + iAttrs.name + "</h1>");
                                    }
                                }
                            },
                        };
                    });
            </script>    
        </head>
        <body>
            <div hello name="Palpit"></div>
        </body>
    </html>
    cs







      이전 예제의 Hello 지시자보다 훨씬 많은 설정 정보가 들어간 것을 볼 수 있습니다. 







      - name(문자열): 지시자에서 사용하는 scope의 이름입니다. 기본적으로 지시자의 이름이 scope의 이름이 됩니다.


      - priority(숫자): 지시자가 적용된 DOM에 여러 지시자가 적용될 수 있는데, 이 지시자 중에서 우선순위를 줄 수 있습니다. 큰 숫자가 먼저 호출됩니다.

     

      - terminal(true/false): 값이 true이면 지시자는 마지막에 호출됩니다. 기본값은 false 입니다.

     

      - scope(true/false/객체): 지시자에서 사용하는 scope 객체에 대한 설정 정보를 줍니다. true는 해당 지시자가 필요로 하는 새로운 scope 객체가 생성되고, false는 해당 지시자가 적용된 DOM은 별도의 scope를 생성하지 않습니다. 해당 지시자를 감싸고 있는 부모 DOM에서 사용하는 scope를 사용하게 됩니다. 대부분 ng-controller에서 생성한 scope를 사용합니다. 객체는 객체를 선언하면 새로운 scope를 생성합니다. 하지만 부모 scope와 상속은 없는 완전 독립된 scope 객체가 생성됩니다. 


      - controller(함수): 지시자에서 사용할 컨트롤러 함수로 위에서 언급한 인자를 받습니다.


      - restrict("EACM" 문자 조합): 지시자가 호출되는 방식을 결정합니다. E의 경우 요소명을 이용한 호출, A는 속성을 이용한 호출, C는 클래스, M은 코멘트를 이용한 호출입니다.


      - template(문자열/함수): 지시자의 템플릿을 설정합니다. 지시자를 적용한 DOM은 해당 템플릿으로 대체됩니다. 대신 DOM의 속성과 CSS 클래스는 모두 복사됩니다. 함수로 작성하게 되면 tElement와 tAttrs을 인자로 받을 수 있습니다.


      - templateUrl(문자열/함수): template와 똑같은 역할을 합니다. 단, 템플릿을 주어진 url을 이용해 읽어들입니다.


      - replace(true/false): 적용된 HTML 요소를 교체할지 여부를 결정합니다. true로 값을 주면 적용된 요소 자체가 템플릿으로 교체되고 false로 값을 주면 요소 자체는 바뀌지 않고 내부 내용만 바뀝니다.


      - transclude(true/'element'): 적용된 HTML 요소의 내부 내용을 컴파일하여 지시자에서 사용할 수 있게 합니다. 컴파일한다는 의미는 내부 내용에 표현식이 작성됐거나 다른 지시자가 사용되었으면 해당 표현식의 계산이 되고 다른 지시자와 연결된 지시자 함수가 실행되어 그 결과가 내부 내용에 반영된다는 것입니다. 


      - compile(함수): 컴파일 함수를 정의합니다. 해당 함수에서는 적용된 DOM을 변경하는 데 사용합니다. compile 함수는 반드시 link 함수나 pre와 post 속성을으로 가지는 객체를 반환해야합니다.




      3-3. 자체 템플릿을 가지는 지시자

       앞에서 만든 Hello 지시자를 좀 더 업그레이드 해보도록 하겠습니다. 이번에는 링크 함수에서 DOM을 직접 생성하지 않고 template 설정을 이용해 생성하려는 DOM을 템플릿화 해보겠습니다.



     * ajsdirect4.html


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <!doctype html>
    <html ng-app="sample">
        <head>
            <meta charset="UTF-8"/>
            <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
            <script>
                angular.module('sample', []).
                    directive('hello'function() {
                        return {
                            template: "<h1>hello <span>name</span></h1>",
                            restrict: "AE",
                            link: function link(scope, iEl, iAt, ctrl) {
                                iEl.find("span").text(iAt.name);
                            }
                        };
                    });
            </script>    
        </head>
        <body>
            <div hello name="Palpit"></div>
        </body>
    </html>
    cs










       예전에는 링크 함수에서 DOM을 생성했다면 지금은 template 설정으로 해당 지시자의 템플릿을 변경했습니다. 하지만 위 예제의 아쉬운 점은 템플릿을 변경하려면 지시자 함수가 정의된 자바스크립트 소스를 직접 수정해야 한다는 것이고, 링크 함수에서 템플릿의 마크업 구조를 정확히 알고 있어야 한다는 점입니다. 


       이러한 점을 보충하기 위해 templateUrl로 변경해서 작성해 보겠습니다. 


     * template/helloTpl.html


    1
    <h1>hello <span>{{name}}</span></h1>
    cs


     * ajsdirect5.html


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!doctype html>
    <html ng-app="sample">
        <head>
            <meta charset="UTF-8"/>
            <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
            <script>
                angular.module('sample', []).
                    directive('hello'function() {
                        return {
                            templateUrl: "template/helloTpl.html",
                            restrict: "AE",
                            controller: function ($scope, $element, $attrs, $transclude) {
                                $scope.name = $attrs.name;
                            }
                        };
                    });
            </script>    
        </head>
        <body>
            <div hello name="Bob"></div>
            <div hello name="Jack"></div>
            <div hello name="Palpit"></div>
        </body>
    </html>
    cs




       이렇게 해서 지시자 정의 코드와 템플릿 코드를 깔끔하게 분리할 수 있습니다. 또한 문서 구조를 정확히 알지 않아도 됩니다. 하지만, 결과값은 맨 마지막의 scope를 사용해서 같은 결과를 출력하게 됩니다. 다음 장에서 scope 설정을 통하여 위 문제를 해결해 봅시다.






    end






    * 이 포스팅은 '시작하세요! AngularJS 프로그래밍'을 바탕으로 작성하였습니다.

    댓글

Designed by Tistory.