ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [AngularJS] 7-2. 지시자(Directive) 응용 - AngularJS 강좌
    Web/AngularJS 2016. 4. 5. 13:35

    지시자(Directive) - 2



      3-4. scope 설정 완전 정복

       scope를 잘못 설정하면 의도치 않은 결과가 발생하므로 분명한 의도에 맞는 scope를 설정해야 합니다. 가령 지시자를 재사용할 수 있는 컴포넌트를 생각하고 만들려면 지시자가 사용하는 scope는 다른 지시자와 독립적인 공간이어야 하고, 같은 지시자가 하나의 페이지에서 여러 번 사용되어도 서로 영향을 주지 않아야합니다. 그럼 상황별로 scope를 설정하는 방법을 알아봅시다.


      부모 scope를 상속받는 scope 설정

        지시자 설정 객체에 scope 속성에 true 값을 주면 부모 scope를 상속받는 새로운 scope가 생성됩니다. hello 지시자에 scope 설정을 true로 한 예제 코드입니다.


      * ajsdirect6.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
    <!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', []).
                    controller('demoCtrl', ['$scope'function($scope) {
                        $scope.name = "Ctrl에서 사용된 name 모델";
                    }]).
                    directive('hello'function() {
                        return {
                            templateUrl: "template/helloTpl.html",
                            restrict: "AE",
                            scope: true,
                            controller: function ($scope, $element, $attrs, $transclude) {
                                if ($attrs.name) $scope.name = $attrs.name;
                            }
                        };
                    });
            </script>    
        </head>
        <body ng-controller="demoCtrl">
            <div hello name="Bob"></div>
            <div hello name="Jack"></div>
            <div hello></div>
        </body>
    </html>
    cs







        위 예제에서는 hello 지시자 객체에 scope 값을 true로 주었습니다. 이전 예제와는 반대로 각각 내용이 출력되는 것을 확인 하실 수 있습니다. 


        하지만 마지막 hello 지시자를 보면 부모 scope로 상속받은 name이 출력되었습니다. 이처럼 신경을 쓰지 않으면 위와같은 결과를 초래하게 됩니다. 따라서 독립 scope 설정을 유념해둬야 합니다.









      @을 이용한 독립 scope 설정

        독립 scope를 설정하려면 지시자 설정 객체의 scope 속성에 객체 리터럴을 이용해 객체를 선언해 주면 됩니다. 그리고 해당 객체에서 부모 객체와의 연관성을 정의합니다.


    1
    scope : { }
    cs


      

        부모 객체와 연관된 지시자 내부 scope에서 사용할 scope 객체 속성명에 "@" 혹은 "@연결된 DOM 속성명"을 값으로 줄 수 있습니다.


    1
    scope : { name"@" } 혹은 scope: { name"@to" }
    cs



        그러면 좀 더 발전된 예제를 보도록 합시다.



     * ajsdirect7.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
    <!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', []).
                    controller('demoCtrl', ['$scope'function($scope) {
                        $scope.name = "Ctrl에서 사용된 name 모델";
                    }]).
                    directive('hello'function() {
                        return {
                            templateUrl: "template/helloTpl.html",
                            restrict: "AE",
                            scope: { name"@to" }    
                        };
                    });
            </script>    
        </head>
        <body ng-controller="demoCtrl">
            <div hello to="Bob"></div>
            <div hello to="Jack"></div>
            <div hello></div>
        </body>
    </html>
    cs








        독립된 scope를 사용하므로 부모 scope의 name 속성을 상속받지 않습니다. 또한 scope 설정에서 지시자 내부 scope의 name 속성에 "@to"을 주어 지시자가 적용된 <div> 태그의 to 속성에 대한 값이 지시자 내부 scope의 name 속성으로 연결되었습니다. 그래서 이전의 바인딩을 위한 코드는 제거된 것입니다. 이 예제를 토대로 응용하여 리스트를 loop 시켜서 출력 가능하기도 합니다.


    1
    2
    $scope.helloList = [{name'palpit'}, {name'Bob'}, {name'Jack'}];
     
    cs



        템플릿에서 아래와 같이 작성하면 반복해서 위 name을 연결합니다.



    1
    <div ng-repeat="hello in helloList" hello to="{{ hello.name }}"></div>
    cs




      &을 이용한 독립 scope 설정

        scope 설정에서 속성의 값으로 "&"나 "&연결된 DOM 속성명"을 주면 부모 scoep의 환경에서 실행될 수 있는 표현식에 대한 레퍼런스를 가지고 올 수 있습니다. 아래 예제는 레퍼런스를 이용한 응용 예제입니다.



     * ajsdirect8.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
    <!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', []).
                    controller('demoCtrl', ['$scope'function($scope) {
                        $scope.helloList = [{name'Palpit'} , {name'Jack'}, {name'Jolie'}];
                        $scope.sendMsg = function(toSb) {
                            console.log(toSb + "에게 메시지를 보냄");
                        };
                    }]).
                    directive('hello'function() {
                        return {
                            templateUrl: "template/helloTpl2.html",
                            restrict: "AE",
                            scope: { 
                                name"@to",
                                send: "&"
                            }    
                        };
                    });
            </script>    
        </head>
        <body ng-controller="demoCtrl">
        <div ng-repeat="helloSb in helloList" hello to="{{helloSb.name}}" 
            send="sendMsg(helloSb.name)"></div>
        </body>
    </html>
    cs



     * template/helloTpl2.html


    1
    <h1>hello <span>{{name}}</span><button ng-click="send()">message Send</h1>
    cs








        바뀐 템플릿을 보면 <button>이 환영 문구 오른쪽에 추가되고 해당 버튼을 클릭하면 "send()" 표현식을 계산하게 됩니다. 그러면 $scope.send의 값에 () 연산을 하여 결국 함수를 호출하게 되는데 이 send는 지시자 설정 객체의 scope에서 "&"로 설정되었습니다.


        그래서 호출할 함수의 레퍼런스는 이 지시자가 적용된 DOM의 send 속성 값을 함수의 내용으로 하는 함수의 레퍼런스가 됩니다. 즉, 가상의 function() { sendMsg(helloSb); } 가 있고 이 함수의 레퍼런스를 지시자 내부의 send가 갖게 되는 것입니다. 그리고 가상의 함수 function() { sendMsg(helloSb); }는 부모 scope의 컨텍스트에서 실행됩니다. 여기서 부모 scope는 ng-controller에서 만들어지는 scope가 아니라 ng-repeat으로 인해 만들어지는 scope입니다. 그래서 { helloSb: "..." } 로 만들어진 scope를 컨텍스트로 하여 실행되는 console.log(toSb + "메시지를 보냄");에 helloList의 인자가 전달되고 콘솔 창에 보이게 되는 것입니다.



      =을 이용한 독립 scope 설정

        scope 설정에서 속성의 값으로 "="이나 "=연결된 DOM 속성명"을 주면 부모 scope의 특정 속성과 지시자 내부 scope의 속성과 양방향 데이터 바인딩을 합니다.



     * ajsdirect9.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
    <!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', []).
                    controller('demoCtrl', ['$scope'function($scope) {
                        $scope.helloList = [{name'Palpit'} , {name'Jack'}, {name'Jolie'}];
                        $scope.sendMsg = function(toSb) {
                            console.log(toSb + "에게 메시지를 보냄");
                        };
                    }]).
                    directive('hello'function() {
                        return {
                            templateUrl: "template/helloTpl3.html",
                            restrict: "AE",
                            scope: { 
                                name"=to"
                            }    
                        };
                    });
            </script>    
        </head>
        <body ng-controller="demoCtrl">
            <ui>
                <li ng-repeat="helloSb in helloList">
                    <input type="text" ng-model="helloSb.name">
                </li>
            </ui>
            <div ng-repeat="helloSb in helloList" hello to="helloSb.name"></div>
        </body>
    </html>
    cs



     * template/helloTpl3.html


    1
    <h1>hello <span>{{name}}</span><input type="text" ng-model="name"></h1>
    cs









       

        위 예제는 어느 하나의 <input> 태그를 수정하면 바인딩된 태그까지 같이 수정됨을 확인 할 수 있습니다.





      ngTransclude와 transclude 설정

        AJS에서 지시자를 이용하면 ng-click과 같이 특정 DOM에 이벤트를 연결하거나 ng-repeat처럼 DOM을 조작하는 일을 할 수 있습니다. 하지만 hello 지시자처럼 재사용할 수 있는 기능이 있는 컴포넌트를 만들 수 있습니다. 


        하지만 다른 컴포넌트를 포함하거나 다른 DOM을 담고 있는 컨테이너를 만들 때도 사용할 수 있습니다. 위젯과 패널의 종류가 이에 속합니다. 위젯과 패널을 지시자로 만들기 위해서는 transclude 설정이 필요합니다. transclude에 true 값을 주면 템플릿에서 ng-transclude라고 설정된 부분에 <panel> ㅌ태그의 내용이 복사됩니다.





      외부 라이브러리를 이용한 지시자 개발

        이번에는 jQuery 플러그인 중에서 간단하게 미니 차트를 만들어주는 스파크라인(sparkline)을 이용해서 지시자 예제를 진행해 보겠습니다. 



     * ajssparkline.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
    <!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 src="https://code.jquery.com/jquery-1.12.2.min.js" 
                integrity="sha256-lZFHibXzMHo3GGeehn1hudTAP3Sc0uKXBXAzHX1sjtk=" crossorigin="anonymous"></script>
            <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-sparklines/2.1.2/jquery.sparkline.js"></script>
            <script>
                angular.module('sample', []).
                    directive('ngSparkline'function() {
                        return {
                            restrict: "EA",
                            scope: {
                                chartData: "@"
                            },
                            link: function(scope, iElement, iAttrs) {
                                var chartData = scope.$eval(scope.chartData),
                                    options = {
                                        type: iAttrs.chartType || 'line'
                                    };
                                jQuery(iElement).sparkline(chartData, options);
                            }
                        };
                    });
            </script>    
        </head>
        <body>
            <ng-sparkline chart-data="[5, 33, 22, 15, 19, 40]" chart-type="bar"></ng-sparkline>
            <div ng-sparkline chart-data="[21, 15, 9, 11, 33]"></div>
        </body>
    </html>
    cs








        위 예제를 보면 ng-sparkline 지시자의 restrict 옵션을 EA로 주었기 때문에 요소 이름이나 속성 이름으로서 지시자를 사용할 수 있습니다. 그리고 별도의 scope를 주었기 때문에 지시자마다 고유한 scope가 있고 @를 써서 chart-data 속성의 문자열 값을 연결했습니다.


        eval 메서드를 통해서 문자열 값을 차트 데이터에 맞게 계산하여 변환시킵니다. 




      데이터 바인딩 기능을 지원하는 컴포넌트 만들기

        위에서 만든 스파크라인을 좀 더 동적인 예제로 만들어 봅시다. 



     * ajsdyspark.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
    40
    <!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 src="https://code.jquery.com/jquery-1.12.2.min.js"
                integrity="sha256-lZFHibXzMHo3GGeehn1hudTAP3Sc0uKXBXAzHX1sjtk=" crossorigin="anonymous"></script>
            <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-sparklines/2.1.2/jquery.sparkline.js"></script>
            <script>
                angular.module('sample', []).
                    directive('ngSparkline', [function () {
                        return {
                            restrict: 'EA',
                            scope: {
                                chartData: "="
                            },
                            link: function (scope, iElement, iAttrs) {
                                var options = {type: iAttrs.chartType || 'line'};
                                scope.$watch('chartData'function(bfData, afData) {
                                    jQuery(iElement).sparkline(scope.chartData, options);
                                }, true);
                            }
                        };
                    }]).
                    controller('sampleCtrl'function($scope) {
                        $scope.chartData = [000];
                    });
            </script>    
        </head>
        <body ng-controller="sampleCtrl">
            <div>
                <input type="number" ng-model="chartData[0]">
                <input type="number" ng-model="chartData[1]">
                <input type="number" ng-model="chartData[2]">
                <br/>
                <p> 데이터: {{chartData}} </p>
            </div>
            <ng-sparkline chart-data="chartData" chart-type="bar"></ng-sparkline>
        </body>
    </html>
    cs









    end


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

    댓글

Designed by Tistory.