Web/AngularJS 검색 결과

28개 발견
  1. 미리보기
    2016.04.08 - Palpit

    [AngularJS] 9. 필터(Filter) 사용 - AngularJS 강좌

  2. 미리보기
    2016.04.08 - Palpit

    [AngularJS] 8. 의존관계 주입과 서비스 - AngularJS 강좌

  3. 미리보기
    2016.04.05 - Palpit

    [AngularJS] 7-2. 지시자(Directive) 응용 - AngularJS 강좌

  4. 미리보기
    2016.04.04 - Palpit

    [AngularJS] 7-1. 지시자(Directive) 기본 - AngularJS 강좌

  5. 미리보기
    2016.04.01 - Palpit

    [AngularJS] 6. 모듈(Module) - AngularJS 강좌

  6. 미리보기
    2016.04.01 - Palpit

    [AngularJS] 5. MVC - 모델, 뷰, 컨트롤러 - AngularJS 강좌

  7. 미리보기
    2016.02.06 - Palpit

    [AngularJS] 4. 템플릿 시스템과 데이터 바인딩 - 2 - AngularJS 강좌

  8. 미리보기
    2016.01.29 - Palpit

    [AngularJS] 4. 템플릿 시스템과 데이터 바인딩 - 1 - AngularJS 강좌

조회수 확인

필터(Filter) 사용

 AJS 필터는 크게 두 가지 용도로 사용됩니다.


 첫번째로 데이터를 보이는 폼을 바꾸는 데 사용됩니다.


 두번째로 여러 데이터 중 조건에 맞는 데이터만 보여줄 때 사용됩니다.


 즉, 포맷팅(Formatting)과 필터링(Filtering)하는데 사용된다고 볼 수 있습니다.




 1. AngularJS에서 제공하는 필터

  아래는 AJS에서 제공하는 필터와 그에 대한 설명입니다.





[출처: AngularJS]




  그럼 예제를 통해서 포맷팅 필터를 살펴보도록 합시다.


 * ajsFilter.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
<!doctype html>
<html ng-app="filterExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script type="text/javascript">
            angular.module('filterExam', []).
                controller('mainCtrl', ['$scope'function($scope) {
                    $scope.cValue = 19900;
                    $scope.dNow = new Date();
                    $scope.jObj = {name'Palpit'};
                    $scope.lString = 'Palpit Moon';
                    $scope.nValue = 10 / 3;
                }]);
        </script>
    </head>
    <body>
        <div ng-controller="mainCtrl">
            <h1>필터 적용 예제</h1>
            <h2>Currency 필터</h2>
            <p> Before: {{cValue}}</p>
            <p> After: {{cValue | currency}}</p>
            <h2>date 필터</h2>
            <p> Before: {{dNow}}</p>
            <p> After: {{dNow | date:'yyyy년 mm월 dd일'}}</p>
            <h2>JSON 필터</h2>
            <p> Before: {{jObj}}</p>
            <p> After: {{jObj | json}}</p>
            <h2>lowercase 필터</h2>
            <p> Before: {{lString}}</p>
            <p> After: {{lString | lowercase}}</p>
            <h2>number 필터</h2>
            <p> Before: {{nValue}}</p>
            <p> After: {{nValue | number:5}}</p>
        </div>
    </body>
</html>
cs





  각 필터가 주어진 데이터를 포맷팅하여 화면에 출력합니다. 


  다음은 필터를 이용해 여러 데이터를 필터링하여 보여주는 filter, limitTo, orderBy 필터 예제를 보도록 합시다.



 * ajsFilter2.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
41
42
43
44
45
46
47
48
49
50
51
52
53
<!doctype html>
<html ng-app="filterExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script type="text/javascript">
            angular.module('filterExam', []).
                controller('mainCtrl', ['$scope'function($scope) {
                    $scope.userList = [
                        {userId: 'pal', userName: 'Jolie', userEmail: 'pal@ajs.com'},
                        {userId: 'jake', userName: 'Anthem', userEmail: 'jake@ajs.com'},
                        {userId: 'paul', userName: 'Paul', userEmail: 'paul@ajs.com'},
                        {userId: 'kino', userName: 'Kino', userEmail: 'kino@ajs.com'},
                        {userId: 'ash', userName: 'Ashey', userEmail: 'ash@ajs.com'},
                        {userId: 'xeo', userName: 'XeoroX', userEmail: 'xeo@ajs.com'},
                    ];
                }]);
        </script>
    </head>
    <body>
        <div ng-controller="mainCtrl">
            <h1> 필터 사용 예제 </h1>
            <div>사용자 이름: <input type="text" ng-model="search.userName"></div>
            <ul>
                <li ng-repeat="user in userList | filter: search">
                    <p>{{user.userId}} | {{user.userName}} | {{user.userEmail}}</p>
                </li
            </ul>
            <h1> limitTo 예제 </h1>
            <div>제한 개수: <input type="number" ng-model="limitNum"></div>
            <ul>
                <li ng-repeat="user in userList | limitTo: limitNum">
                    <p>{{user.userId}} | {{user.userName}} | {{user.userEmail}}</p>
                </li
            </ul>
            <h1> orderBy 예제 </h1>
            <div>
                정렬 순서:
                아이디 <input type="radio" ng-model="order" value="userId">
                이름 <input type="radio" ng-model="order" value="userName">
                <br/>
                역순 여부:
                <input type="checkbox" ng-model="reverse">
            </div>
            <ul>
                <li ng-repeat="user in userList | orderBy:order:reverse">
                    <p>{{user.userId}} | {{user.userName}} | {{user.userEmail}}</p>
                </li
            </ul>
 
        </div>
    </body>
</html>
cs





  위 두 예제에서는 모두 템플릿에서 필터를 사용했습니다. 하지만 템플릿이 아닌 컨트롤러나 서비스, 지시자에서도 필터를 사용할 수 있습니다. 


  필터를 템플릿 외에 자바스크립트에서 사용하려면 $filter 서비스를 이용해야 합니다. 



 * ajsFilter3.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
41
42
43
<!doctype html>
<html ng-app="filterExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script type="text/javascript">
            angular.module('filterExam', []).
                controller('mainCtrl', ['$scope''$filter'function($scope, $filter) {
                    var userList = [
                        {userId: 'pal', userName: 'Jolie', userEmail: 'pal@ajs.com'},
                        {userId: 'jake', userName: 'Anthem', userEmail: 'jake@ajs.com'},
                        {userId: 'paul', userName: 'Paul', userEmail: 'paul@ajs.com'},
                        {userId: 'kino', userName: 'Kino', userEmail: 'kino@ajs.com'},
                        {userId: 'ash', userName: 'Ashey', userEmail: 'ash@ajs.com'},
                        {userId: 'xeo', userName: 'XeoroX', userEmail: 'xeo@ajs.com'},
                    ];
 
                    $scope.value = new Date();
                    $scope.dateFormatedValue = $filter('date')($scope.value, 'yyyy-mm-dd');
                    $scope.userList = userList;
                    $scope.filter = function(filterObj) {
                        $scope.userList = $filter('filter')(userList, filterObj);
                    };
                }]);
        </script>
    </head>
    <body>
        <div ng-controller="mainCtrl">
            <p> 필터 사용 전 날짜 데이터: {{value}} </p>
            <p> date 필터 사용: {{dateFormatedValue}} </p>
            <hr>
            <div>
                사용자 이름: <input type="text" ng-model="search.userName">
                <button ng-click="filter(search)">필터 처리</button>
            </div>
            <ul>
                <li ng-repeat="user in userList">
                    <p>{{user.userId}} | {{user.userName}} | {{user.userEmail}}</p>
                </li>
            </ul>
        </div>
    </body>
</html>
cs









  mainCtrl 함수에서 $filter 서비스를 주입받는 것을 볼 수 있습니다. 이 $filter 서비스에서 사용하고자 하는 필터 이름을 인자로 주고 함수를 호출하면 해당 필터 함수를 가지고 올 수 있습니다. AJS는 $filter 서비르를 이용해 등록된 필터 함수를 얻을 수 있고 이렇게 얻은 필터 함수를 이용해 특정 데이터를 포맷하거나 필터 처리 할 수 있습니다.





 2. 필터 만들기

   AJS는 다양한 내장 필터를 제공하지만, 원하는 필터 개발도 가능합니다. 개발을 하기 위해서는 $filterProvider를 사용하면 됩니다.


   $filterProvider에서 register 메서드를 이용해 사용자 정의 필터를 등록할 수 있습니다. 


  그럼 예제를 통해 $filterProvider의 register 메서드 사용법을 알아보도록 합시다.


 * ajsFilter4.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="filterExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script type="text/javascript">
            angular.module('filterExam', []).
                config(function($filterProvider) {
                    $filterProvider.register('capitalize'function() {
                        return function(text) {
                            return text.charAt(0).toUpperCase() + text.slice(1);
                        };
                    });
                }).
                controller('mainCtrl', ['$scope'function($scope) {
                    $scope.text = "temp";
                }]);
        </script>
    </head>
    <body>
        <div ng-controller="mainCtrl">
            <p>{{text | capitalize}}</p>
        </div>
    </body>
</html>
cs







 

end


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

다른 카테고리의 글 목록

Web/AngularJS 카테고리의 포스트를 톺아봅니다
조회수 확인

의존관계 주입과 서비스

 위 개념은 AngularJS의 중요한 개념 중 하나입니다. 의존관계 주입은 AJS 기반의 애플리케이션을 개발하는 데 있어서 항상 있기 마련이고 서비스가 없는 AJS 웹 앱이란 상상하기 어렵습니다. 


 1. AJS에서의 의존관계 주입

  하나의 객체가 다른 객체를 사용하는 순간 의존관계가 성립됩니다. AJS 개발자 문서에는 자바스크립트 상에서 객체들 사이의 의존관계가 크게 3 가지 경우에 생성된다고 합니다.

  

  - new 키워드

  - 전역변수 참조

  - 인자를 통하여 참조를 전달



  처음 1번과 2번은 의존관계가 강하게 연결되었고 3번은 느슨하게 연결되었다고 말할 수 있습니다. 아래 예시를 통해 확인해봅시다.



1
2
3
4
function demoCtrl() {
    var bookmark = new BookmarkRes(new Ajax(), new JsonParser());
}
 
cs




  위 데모 컨트롤러 함수는 BookmarkRes 함수를 사용하고 있습니다. 하지만 데모 컨트롤러 함수는 BookmarkRes 함수가 어떻게 bookmark 객체를 생성해야 하는지 정확히 알고 있어야 한다는 문제점이 있습니다. 즉, Ajax와 JsonParser 인자까지 알고 있어야 한다는 점입니다. 다음은 전역변수 참조를 통해 개선한 코드입니다.



1
2
3
4
5
6
7
8
9
10
11
var factory = {
    getBookmarkRes: function() {
        return new BookmarkRes(factory.getAjax(), factory.getJsonParser());
    },
    getAjax: function() { return new Ajax(); },
    getJsonParser: function() { return new JsonParser(); }
}
 
function demoCtrl() {
    var bookmark = factory.getBookmarkRes();
}
cs




  전역변수 영역에 BookmarkRes 가 객체를 생성하는 역할을 담당하는 팩토리 객체를 선언하고 BookmarkRes는 팩토리의 getAjax(), getJsonParser() 함수를 호출하여 필요한 객체를 얻을 수 있게 되었습니다. 위 코드에서의 문제점은 각각의 기능에 대해서 단위 테스트 하기가 어렵다는 점입니다. 그러면 마지막으로 인자 참조를 통한 예제를 보도록 하겠습니다.



1
2
3
function demoCtrl (BookmarkRes) {
    var bookmark = BookmarkRes.get();
}
cs




  위 코드는 데모 컨트롤러가 BookmarkRes를 인자로 전달받아 의존관계가 성립된 것을 볼 수 있습니다. 데모 컨트롤러는 BookmarkRes가 어떻게 생성되는지 알 필요도 없고 단위 테스트에서도 얼마든지 필요한 테스트용 BookmarkRes를 주입받을 수 있게 되는 것입니다. 


  이처럼 AJS에서 주입되는 대상을 서비스라 하여 BookmarkRes를 서비스로서 개발하고 이를 컨트롤러나 다른 서비스 혹은 지시자 등에 주입되는 방식인 DI를 이용해 컴포넌트 별 의존 관계를 정의 할 수 있습니다.




  Module.factory를 이용한 Hello 서비스 만들기

   그러면 AJS의 서비스를 정의하는 방법을 간단한 예제를 통해서 알아보도록 합시다. 서비스를 만들려면 모듈 인스턴스가 필요합니다. 


 * ajsfactory.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
<!doctype html>
<html ng-app="factoryExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script>
            angular.module('factoryExam', []).
                factory('hello', [function() {
                    var helloText = "님 안녕하세요.";
 
                    return {
                        say: function(name) {
                            return name + helloText;
                        }
                    };
                }]).
                controller('mainCtrl'function($scope, hello) {
                    $scope.hello = hello.say("Pal");
                });
        </script>
    </head>
    <body>
        <div ng-controller="mainCtrl">
            <p>{{hello}}</p>
        </div>
    </body>
</html>
cs






   위 예제 코드를 보면 angular.module() 함수를 이용해 factoryExam 모듈을 선언하고 해당 인스턴스의 factory() 메서드를 호출해 hello 서비스를 만들었습니다. factory() 함수의 첫 번째 파라미터로 서비스 명을 주고 다음 인자로 서비스를 주입받을 때 반환할 객체를 설정하는 팩토리 함수를 줍니다. 이렇게 만든 hello 서비스는 컨트롤러에서 인자로 주입해 사용할 수 있습니다.





[출처: AngularJS]



   위 예제에서 hello 서비스를 angular.module('factoryExam', []).factory() 메서드를 이용해서 정의했습니다. 사실 모듈 API의 factory() 메서드는 $provide.factory() 메서드와 같습니다. 이렇게 정의된 서비스는 $injector로 얻어오고 각 서비스의 의존 관계도 $injector에 의해서 생성되게 됩니다. 서비스를 특정 함수에게 주입하거나 등록된 서비스를 얻어오는 역할을 $injector가 하는 것입니다. $injector는 서비스의 싱글톤을 유지하기 위해 내부적으로 캐시를 가지고 있어 서비스가 매번 새로운 객체를 생성하는 것을 방지합니다. 그럼 $provide와 $injector에 대해 자세히 보도록 합시다.



  $provide를 이용한 Provider 정의

   AJS에서 $provide를 이용해 주입할 수 있는 서비스를 제공해 주는 프로바이더를 정의할 수 있습니다. 이 서비스 프로바이더가 특정 서비스를 제공해 주는 것입니다. 그래서 서비스를 단순하게 생각하면 $provide로 정의한 프로바이더가 생성하는 객체라고 할 수 있습니다. 이런 서비스를 만드는 방법은 크게 5 가지 방법입니다.


   - value로 정의하는 방법

    웹 앱을 개발하다 보면 앱 전역에서 사용하는 특별한 값이 필요한 경우가 있습니다. 가령 앱 이름이나 컨텍스트 루트가 그러할 것입니다. 이러한 값을 반환하는 서비스를 만들어 봅시다.


 * ajsvalue.html


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype html>
<html ng-app="valueExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script>
            angular.module('valueExam', []).
                value('AppName''Value Exam').
                controller('mainCtrl'function($scope, AppName) {
                    $scope.AppName = AppName;
                });
        </script>
    </head>
    <body ng-controller="mainCtrl">
        <p> 어플리케이션 이름 : {{AppName}} </p>
    </body>
</html>
cs


   





  valueExam이란 모듈을 만들고 해당 모듈의 value 메서드를 이용해 AppName 서비슬르 만든 것을 볼 수 있습니다. 






   - Factory로 정의하는 방법

   실제로 서비스를 만들게 되면 거의 factory를 이용해 만들게 됩니다. factory는 서비스를 생성하는 로직을 담는 함수입니다. 기본적인 factory의 정의는 아래와 같습니다.


1
module.factory('서비스이름'function([주입받을 서비스]) { ... } );
cs



    모듈 API를 이용해 $provider.factory 메서드를 사용하는 데 value와 다르게 두 번째 인자를 함수로 전달해 줍니다. 해당 함수는 팩토리 함수로서 인자로는 주입받을 다른 서비스를 줄 수 있습니다. 다음 예제는 주어진 이름과 이메일을 추가하고 하단에 목록으로 보여주는 예제입니다.


 * ajsfactory2.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<!doctype html>
<html ng-app="factoryExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script>
            angular.module('factoryExam', []).
                factory('AppNm', [function () {
                    return 'demo App';
                }]).
                factory('UserRes'function() {
                    var userList = [];
 
                    return {
                        addUser: function(newUser) {
                            userList.push(newUser);
                        },
                        updateUser: function(idx, updatedUser) {
                            userList[idx] = updatedUser;
                        },
                        deleteUser: function(idx) {
                            userList[idx] = undefined;
                        },
                        selectUsers: function() {
                            return userList;
                        }
                    }
                }).
                controller('mainCtrl'function($scope, AppNm, UserRes) {
                    $scope.appNm = AppNm;
 
                    $scope.users = UserRes.selectUsers();
 
                    $scope.addNewUser = function(newUser) {
                        UserRes.addUser({
                            name: newUser.name,
                            email: newUser.email
                        });
                    };
                });
        </script>
    </head>
    <body ng-controller="mainCtrl">
        <p> 어플리케이션 이름: {{appNm}} </p>
        <div>
            이름: <input type="text" ng-model="newUser.name">
            이메일: <input type="text" ng-model="newUser.email">
            <button ng-click="addNewUser(newUser)">Add new user</button>
        </div>
        <ul>
            <li ng-repeat="user in users">{{user.name}}, {{user.email}}</li>
        </ul>
    </body>
</html>
cs








  위 예제를 보면 UserRes 서비스를 factory 메서드로 정의하고 있고 addUser, updateUser, deleteUser, selectUsers 메서드를 가지는 객체를 반환하고 있습니다. 또한 UserRes의 userList는 메서드로만 접근 가능합니다. UserRes를 여러 컨트롤러에서 주입받을 수 있지만, UserRes는 싱글톤이므로 반환되는 객체의 메서드들은 모두 같은 userList 배열을 참조하게 됩니다.



   - Service로 정의하는 방법

    JavaScript 특성상 함수를 클래스로 사용하면서 상속과 같은 OOP 특성을 이용해 개발할 수 있습니다. Service 메서드는 이러한 생성자 함수를 객체화할 때 사용합니다. 


    일반적으로 new 키워드를 이용해 인스턴스를 얻게 됩니다. 이러한 new 키워드를 이용한 인스턴스화와 $provide.service 메서드와의 차이점은 매번 다른 새로운 객체를 반환하는 것이 아니라 싱글톤을 유지하여 같은 객체를 반환한다는 것입니다. 그럼 이제 예제를 통해 살펴보도록 합시다.


 * ajsservice.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
41
42
<!doctype html>
<html ng-app="serviceExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script>
        function Calculator() {
            this.lastValue = 0;
 
            this.add = function(a, b) {
                var returnV = a + b;
                this.lastValue = returnV;
 
                return returnV;
            };
 
            this.sub = function(a, b) {
                var returnV = a - b;
                this.lastValue = returnV;
 
                return returnV;
            };
        }
 
            angular.module('serviceExam', []).
                factory('Calculate', [function() {
                    return new Calculator();
                }]).
                service('CalculateS', Calculator).
                controller('mainCtrl'function($scope, Calculate, CalculateS) {
                    $scope.val1 = Calculate.add(154);
                    console.log(Calculate.lastValue);
                    $scope.val2 = CalculateS.sub(154);
                    console.log(CalculateS.lastValue);
                });
        </script>
    </head>
    <body ng-controller="mainCtrl">
        <P> 15 + 4 = {{val1}} </P>
        <p> 15 - 4 = {{val2}} </p>
    </body>
</html>
cs






   위 예제는 factory와 service를 통해 작성된 예제입니다. 위 예제에서 service 쪽 코드를 보면 단순히 생성자 함수 레퍼런스만 전달하여 사용하는 것을 볼 수 있습니다. factory 메서드에 비해서 간단하게 작성이 됩니다. 





   - Provider로 정의하는 방법

    지금까지 $provide의 value, factory, service 메서드를 살펴봤습니다. 위 메서드들은 모두 provider 메서드를 사용해 의미상 alias처럼 제공되는 메서드들입니다. 실제로 서비스를 등록할 때 AJS는 $provide.provider만을 인지하게 됩니다.



 * ajsprovider.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="providerExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script>
            angular.module('providerExam', []).
                provider('Logger', [function() {
                    function Logger(msg) {
                        if (checkNativeLogger) console.log(msg);
                    }
 
                    Logger.debug = function(msg) { if (checkNativeLogger) console.debug(msg); };
                    Logger.info = function(msg) { if (checkNativeLogger) console.info(msg); };
 
                    function checkNativeLogger() {
                        if (consolereturn true;
                        return false;
                    }
 
                    this.$get = [function() {
                        return Logger;
                    }];
                }]).
                controller('mainCtrl'function($scope, Logger) {
                    Logger("console.log에서 출력되는 로그 메시지");
                    Logger.debug("console.debug에서 출력되는 로그 메시지");
                });
        </script>
    </head>
    <body ng-controller="mainCtrl">
    </body>
</html>
cs






   별도의 Logger 서비스를 provider 메서드로 정의하고 있습니다. provider 메서드는 앞에서 본 factory, service와 마찬가지로 첫 번째 인자를 서비스 명을 주고 다음으로 함수를 정의합니다. 이 provider 함수는 꼭 this.$get = [function() {...}]를 포함하고 있어야 합니다. this.$get에는 배열 또는 함수를 줄 수 있는데 배열은 다음과 같이 다른 서비스를 주입할 때 사용합니다.



1
this.$get = ['$window'function(win) { win.console.log("..."); }];
cs



   실제로 factory와 service가 provider를 어떻게 감싸고 있는지 살펴보면 factory는 다음 코드와 같습니다.


1
2
3
4
5
6
7
function factory(name, factory) {
    $provide.provider(namefunction() {
        this.$get = function($injector) {
            return $injector.invoke(factory);
        };
    });
}
cs



   factory 메서드는 결국 전달받은 팩토리 함수를 실행하고 그 결과를 반환하는 것입니다. 다음으로 service 메서드는 다음코드와 같습니다.


1
2
3
4
5
6
7
function service(name, Class) {
    $provide.provider(namefunction() {
        this.$get = function($injector) {
            return $injector.instantiate(Class);
        };
    });
}
cs



   service 메서드는 전달받은 생성자 함수를 인스턴스화하고 그 결과인 인스턴스 객체를 반환합니다. 


   다음으로 방금 정의한 서비스 프로바이더 자체를 주입받아 서비스를 주입하기 전에 설정하는 방법에 대해 살펴보도록 합시다.



   - 서비스 프로바이더 설정하기

   사용자 정의 서비스는 대부분 factory나 service 메서드로 정의하는데 특별한 경우에는 provider 메서드를 이용해야만 합니다. 바로 서비스를 주입하기 전에 별도의 설정을 해야할 때가 그렇습니다. 


   이때 Module API의 config 함수를 사용할 수 있는데 config 함수를 사용하려면 서비스 프로바이더가 설정할 수 있게 만들어져 있어야 합니다. 위 예제의 Logger를 로깅 레벨을 설정할 수 있게 변경해 봅시다.



 * ajsprovider.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 
<!doctype html>
<html ng-app="providerExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script>
            angular.module('providerExam', []).
                provider('Logger', [function() {
                    var defaultLogLevel = 'log';
 
                    function Logger(msg) {
                        if (checkNativeLogger) {
                            if (defaultLogLevel === "debug") {
                                console.debug(msg);
 
                                return;
                            } 
                            if (defaultLogLevel === "info") {
                                console.info(msg);
 
                                return;
                            }
 
                            console.log(msg);
                        }
                    }
 
                    Logger.debug = function(msg) { if (checkNativeLogger) console.debug(msg); };
                    Logger.info = function(msg) { if (checkNativeLogger) console.info(msg); };
 
                    function checkNativeLogger() {
                        if (consolereturn true;
                        return false;
                    }
 
                    this.setDefaultLevel = function(level) {
                        switch(level) {
                            case 'debug':
                                defaultLogLevel = 'debug';
                                break;
 
                            case 'info':    
                                defaultLogLevel = 'info';
                                break;
 
                            default:
                                defaultLogLevel = 'log';
                        }
                    };
 
                    this.$get = [function() {
                        return Logger;
                    }];
                }]).
                config(['LoggerProvider'function (LoggerProvider) {
                    LoggerProvider.setDefaultLevel('debug');
                }]).
                controller('mainCtrl'function($scope, Logger) {
                    Logger("console.log에서 출력되는 로그 메시지");
                    Logger.debug("console.debug에서 출력되는 로그 메시지");
                });
        </script>
    </head>
    <body ng-controller="mainCtrl">
    </body>
</html>
cs








   Logger 서비스 프로바이더가 setDefaultLevel 메서드로 기본 로깅 레벨을 설정하는 것을 볼 수 있습니다. 



  지금까지 $provider를 이용해 AJS 기반의 웹 앱에 서비스를 제공하는 서비스 프로바이더를 정의하는 방법을 살펴보았습니다. 다음은 AJS에서 제공하는 주요 사항별 각 메서드의 차이점을 정리한 표입니다.







[출처: AngularJS API Guide]




  $injector를 이용한 서비스 주입

   AJS의 $injector는 $provider를 통해 등록된 서비스 프로바이더를 이용해 서비스 인스턴스를 생성하는 역할을 합니다. 그리고 $injector는 AJS 앱이 생성될 때 자동으로 생성되며 하나의 AJS 앱은 하나의 $injector만 가지게 됩니다. 


   $injector를 이용하면 특정 서비스 객체를 얻을 수 있습니다. 또는 특정 서비스가 해당 모듈에 정의되어 있는지 확인할 수도 있습니다. 그러면 간단한 예제로 $injector를 이용해 서비스 객체를 얻거나 확인하는 방법을 알아봅시다.



 * ajsinjector.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="injectorExam">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script>
            angular.module('injectorExam', []).
                factory('Hi', [function() {
                    return {
                        hiTo: function(name) {
                            console.log('hi ' + name);
                        }
                    };
                }]);
 
                var injector = angular.injector(['ng''injectorExam']),
                    hasHi = injector.has('Hi'),
                    hiSvc = null;
 
                if (hasHi) {
                    hiSvc = injector.get('Hi');
                    hiSvc.hiTo("Pal");
                }
        </script>
    </head>
    <body>
    </body>
</html>
cs





   injectorExam 모듈을 선언한 후 angular.injector(['ng', 'injectorExam'])를 이용해 $injector 객체를 가지고 왔습니다. 그리고 $injector의 has 메서드로 주어진 서비스가 ng 모듈과 injectorExam 모듈에 정의되어 있는지 확인합니다. 


   has 메서드로 정의를 확인 후 get 메서드를 통해 Hi 서비스 객체를 얻어와 hiTo 메서드를 통해서 콘솔 출력을 했습니다. 




  의존 관계를 주입 받을 수 있는 곳

   지금까지 컨트롤러 함수나 지시자 설정 함수에서 마법과도 같이 정의된 서비스를 주입받을 수 있었습니다. 하지만 $injector를 살펴본 지금은 AJS 내부에서 이 $injector로 컨트롤러 함수나 설정 함수가 invoke 메서드나 instantiate 메서드에 의해 호출되는 것을 생각할 수 있습니다. 


   다음 목록은 AJS 내부에서 $injector를 이용해 주어진 함수들의 매개변수에 적절한 서비스가 주입되는 곳입니다. 즉, AJS에서 Di 가 가능한 함수라고 볼 수 있습니다.


   - 컨트롤러 정의 함수

   - 지시자 정의 함수

   - 필터 정의 함수

   - $provider 의 provider 정의 함수 내 this.$get에 연결된 함수

   - $provider 의 factory 정의 함수

   - $provider 의 service 정의 함수

   - AJS가 제공하는 서비스




 2. AJS가 제공하는 서비스

  AJS에서는 프레임워크 기능이나 유틸리티적인 기능으로 여러 내장 서비스를 제공합니다. 자세한 내용은 아래 링크를 통해서 확인하시길 바랍니다.


  https://docs.angularjs.org/api  





end


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

다른 카테고리의 글 목록

Web/AngularJS 카테고리의 포스트를 톺아봅니다
조회수 확인

지시자(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 프로그래밍'을 바탕으로 작성하였습니다.

다른 카테고리의 글 목록

Web/AngularJS 카테고리의 포스트를 톺아봅니다
조회수 확인

지시자(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 프로그래밍'을 바탕으로 작성하였습니다.

다른 카테고리의 글 목록

Web/AngularJS 카테고리의 포스트를 톺아봅니다
조회수 확인

모듈(Module)

 모듈은 대체로 관련된 기능을 하나로 묶어 다른 코드와 결합도를 줄이고 재사용성을 높이기 위해 사용합니다.


 최근에 자바스크립트에서는 코드의 복잡도를 낮추기 위해 많은 자바스크립트 라이브러리를 모듈화할 필요성이 생겼고, 모듈을 이용한 개발이 거의 필수가 되었습니다. jQuery 와 같은 많은 라이브러리는 모듈 패턴을 이용해 모듈로 구현되었고 많은 웹 앱 또한 모듈 패턴을 이용해 개발되었습니다.


 AJS에서도 이러한 모듈을 만들 수 있는 기능을 제공하고 별도의 모듈 패턴을 구현할 필요가 없는 API를 제공합니다. 다음은 모듈 선언 코드입니다.



1
2
angular.module("모듈이름", ["사용할 모듈", ...])
 
cs



 angular.module 함수를 사용해 모듈을 만들면 모듈 인스턴스가 반환되는데 해당 모듈 인스턴스는 컨트롤러, 서비스, 지시자, 필터들을 담습니다.


 다음은 모듈 인스턴스가 사용할 수 있는 메서드를 설명한 표입니다.








 모듈의 API를 보면 AJS에서 모듈의 의미를 어느 정도 파악할 수 있습니다. 간단히 말하자면 컨트롤러, 서비스, 필터, 지시자를 담는 그릇이라고 할 수 있겠습니다. 


 하나의 AJS 웹 앱은 하나의 모듈을 지정할 수 있습니다. 해당 모듈은 자바의 메인 메서드와 같은 역할을 하는 run 함수를 이용해 앱 시작에 대한 로직을 작성할 수 있습니다. 그리고 해당 앱에서 사용하는 컨트롤러, 서비스, 필터 그리고 지시자를 등록할 수 있습니다.



 1. 모듈을 이용한 컨트롤러 등록

  지금까지 예제에서는 컨트롤러 함수를 전역(Global) 영역에 선언해 사용했습니다. 


  앞으로는 모듈을 이용해 컨트롤러를 선언하고 해당 모듈을 사용하는 애플리케이션이 등록한 컨트롤러를 사용할 수 있습니다. 간단하게 북마크를 관리하는 컨트롤러를 만들어 봅시다.



 * ajsmodule.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
 
<!doctype html>
<html ng-app="ngBookmark">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script type="text/javascript">
            angular.module('ngBookmark', []).controller("bookmarkListCtrl", ['$scope'function($scope) {
                $scope.bookmarkList = [
                    {id: "blog"name"블로그", url: "palpit.tistory.com"},
                    {id: "google"name"구글", url: "google.com"},
                    {id: "daum"name"다음", url: "daum.net"}
                ];
            }]);
        </script>
    </head>
    <body>
        <div ng-controller="bookmarkListCtrl">
            <h1>북마크 목록</h1>
            <div>
                <ul>
                    <li ng-repeat="bookmark in bookmarkList">
                        <em>{{bookmark.name}}</em>: <span>{{bookmark.url}}</span>
                    </li>
                </ul>
            </div>
        </div>
    </body>
</html>
cs








 - ng-app="ngBookmark"

  ng-app 지시자에는 해당 앱이 사용하는 모듈의 이름을 값으로 줄 수 있습니다.


 - .controller("bookmarkListCtrl", ['$scope', function($scope) {

  이름이 'bookmarkListCtrl'인 컨트롤러 함수를 등록합니다. angular.module() 함수를 사용하면 그 반환 값으로 module 인스턴스가 반환됩니다. 해당 인스턴스는 controller() 함수를 이용해 컨트롤러를 등록할 수 있습니다. controller() 함수의 첫 번째 인자는 컨트롤러 명이고, 두 번째 인자는 배열 또는 함수가 됩니다.




 2. 다른 모듈의 사용

  ng-app에 해당 앱이 사용하는 모듈명을 값으로 주었습니다. 배열로 준 것이 아니라 하나의 값이 들어가므로 하나의 앱에서는 하나의 모듈만 사용할 수 있는 것처럼 보입니다.


  그렇다면 재사용되는 코드의 단위를 좀 더 세세하게 쪼개어 여러 모듈로 만들고 싶을 때는 어떻게 할 수 있을까요?


  모듈을 선언하는 자바스크립트 API를 보면 사용할 모듈의 이름을 배열로 전달할 수 있습니다.


1
2
angular.module("ngBookmark", ["moduleA""moduleB"])
 
cs



  위와 같이 ngBookmark 모듈을 선언할 때 해당 모듈이 참조하는 moduleA와 moduleB를 선언하는 것입니다. 사실 우리가 사용하는 ng-repeat, ng-select와 같은 지시자와 곧 배울 $http, $log와 같은 서비스 모두 기본 모듈인 ng 모듈에서 제공하는 것들입니다.


  하지만 기본 ng 모듈 이외에도 AJS에서 별도의 모듈(ngMock ngCookies, ngResource, ngSanitize)을 제공하고 있습니다. 아래 예제에서 ngCookie 모듈을 사용하는 예제를 살펴보도록 합시다.



 * ajscookie.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="cookieDemo">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular-cookies.js"></script>
        <script type="text/javascript">
            angular.module('cookieDemo', ['ngCookies'])
                .controller("mainCtrl", ['$scope''$cookieStore'function($scope, $cookieStore) {
                    $scope.value = $cookieStore.get("test"|| "없음";
                    $scope.getValue = function() {
                        $scope.value = $cookieStore.get("test");
                    };
                    $scope.putValue = function(iV) {
                        $cookieStore.put("test", iV);
                    };
                }]);
        </script>
    </head>
    <body ng-controller="mainCtrl">
        <h1>쿠키 서비스 사용</h1>
        <p>test 키로 저장된 값: {{value}} </p>
        <button ng-click="getValue()">쿠키 가져오기</button>
        <br/>
        <input type="text" ng-model="iValue"><button ng-click="putValue(iValue)">쿠키 저장</button>
    </body>
</html>
 
cs






 - angular.module('cookieDemo', ['ngCookies'])

  'cookieDemo' 모듈을 선언할 때 ngCookies 모듈을 사용한다는 의미로 'ngCookies' 문자열을 값으로 가지는 배열을 두 번째 인자로 주었습니다.


 - .controller("mainCtrl", ['$scope', '$cookieStore', function($scope, $cookieStore) 

  ngCookies 모듈은 두 개의 서비스를 제공합니다. 하나는 $cookie이고 다른 하나는 $cookieStore입니다. $cookieStore 서비스를 사용하기 위해 컨트롤러 함수에 $cookieStore를 인자로 주었습니다. 




end






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

다른 카테고리의 글 목록

Web/AngularJS 카테고리의 포스트를 톺아봅니다
조회수 확인

MVC - Model, View, Controller

 AngularJS는 자바스크립트 MVC 프레임워크 중 하나입니다. 


 아래는 기본적인 MVC 패턴에 대한 설명입니다.











 - Model

  도메인에 해당하는 정보를 나타내는 오브젝트. 대체로 애플리케이션의 데이터와 행위를 포함하고 있음.

 

 - View

  모델의 정보를 UI에서 보여주는 역할. 하나의 모델을 다양한 뷰에서 사용할 수도 있고, 여러 모델을 하나의 뷰에서 사용할 수 있음


 - Controller

  애플리케이션에서 사용자의 입력을 받아 모델에 변경된 상태를 반영. 이는 모델을 변하게 하여 결국 뷰를 갱신. 컨트롤러는 직접 뷰를 변경하는 것이 아니라 로직을 통해 모델을 변경하고 그 결과가 뷰로 뿌려지는 것.



 MVC 패턴을 이용하면 애플리케이션 개발과 유지보수에 있어서 몇 가지 이점이 있습니다.


 첫째, 사용자 인터페이스와 비지니스 데이터를 분리할 수 있습니다. 따라서 비지니스 데이터에 해당하는 모델을 다른 뷰에서도 사용하여 모델을 재사용할 수 있게 됩니다.


 둘째, MVC 패턴으로 개발함으로써 팀 사이에 표준화된 개발 방식을 제공할 수 있습니다. 




 1. 모델(Model)

  AngularJS에서는 사용자 정보, 도서 정보, 북마크 정보 등 하나의 엔티티(Entity)나 여러 개의 엔티티가 모델이 될 수 있습니다. 그리고 다른 프레임워크와의 차이점은 별다른 상속 없이 순수 자바스크립트 객체가 모델이 될 수 있다는 점 입니다. 

  

  하지만 중요한 점은 이러한 모델 객체는 AngularJS의 $scope 객체로부터 접근할 수 있어야 한다는 것입니다.


  모델은 컨트롤러에서 $scope 객체에 선언하거나 템플릿에서 선언할 수 있습니다. 다음 코드는 컨트롤러에서 모델을 선언한 코드입니다.


1
2
3
4
function mainCtrl($scope) {
    $scope.userId: "palpit";
    $scope.bookmark: { name"내 블로그", url: "palpit.tistory.com", image: "palpit.png" };
}
cs



  위 코드처럼 컨트롤러에서 모델을 선언하고 있습니다. $scope의 속성명은 모델명을 나타내고 값은 모델이 됩니다. 여기서 모델은 단순한 문자열이나 객체, 배열이 될 수 있습니다.


  즉, 모델은 평범한 자바스크립트 객체 입니다. 모델은 HTML 템플릿에서도 선언 할 수 있습니다.




 2. 뷰(View)

  AngularJS에서 뷰는 문서 객체 모델(DOM: Document Object Model)입니다. 


  브라우저에서 HTML 문서를 읽어서 DOM을 생성하는데 AngularJS에서는 이 DOM이 뷰가 되는 것입니다. 템플릿과 뷰를 혼동할 수 있는데 AngularJS에서 HTML 문서는 템플릿이고 이 템플릿을 AngularJS가 읽어서 뷰를 생성합니다. 뷰를 생성하는 과정은 다음과 같습니다.


  a. HTML로 작성한 템플릿을 브라우저가 읽음

  b. 브라우저는 DOM 생성

  c. <script src="angular.js">가 실행되어 AngularJS 소스 실행

  d. DOM 생성 시 DOM Content Loaded 이벤트가 발생하면서 정적 DOM을 읽고 뷰를 컴파일, 컴파일 시 확장 태그나 속성을 읽고 처리한 후 데이터 바인딩

  e. 컴파일 완료되면 동적 DOM, 뷰를 생성













 3. 컨트롤러(Controller)

  AngularJS에서 컨트롤러는 많은 일을 하지 않습니다. 애플리케이션의 로직을 담당하는 역할만 합니다.


  결국 $scope 객체에 데이터나 행위를 선언하는 것입니다. 그리고 컨트롤러는 인자로 $scope를 전달받는 단순한 자바스크립트 함수입니다.


  컨트롤러 코드를 작성할 때 주의할 사항이 있습니다. 컨트롤러는 단 하나의 뷰에 해당하는 애플리케이션의 로직만을 담당해야 합니다. 화면상의 로직이 아니라 애플리케이션의 비즈니스 로직입니다.


  즉, DOM을 조작하는 행위와 같은 화면 상의 로직은 다음에 설명한 지시자에서 구현하고 컨트롤러에서는 애플리케이션의 비즈니스 로직만을 구현해야 합니다.


  또한, 컨트롤러는 하나의 컨트롤러에 하나의 $scope만을 갖게 됩니다. 컨트롤러 당 각 $scope를 갖게 된다는 것 입니다. 각각 독립된 $scope가 생성되고 서로 참조할 수 없게 됩니다. 



 4. $rootScope와 $scope

  $scope는 양방향 데이터 바인딩의 핵심이자 뷰와 컨트롤러를 이어주는 징검다리이기도 합니다. 다음 목록은 AngularJS 애플리케이션에서의 $scope의 특징을 보여줍니다.


  - 뷰와 컨트롤러를 이어주는 다리

  - 연결된 DOM에서의 실행환경

  - 양방향 데이터 바인딩 처리

  - 이벤트 전파 처리

  - 계층적 구조




  4-1. $scope 계층 구조

   모든 AngularJS 애플리케이션은 하나의 $rootScope를 가집니다. 이 $rootScope는 ng-app을 생성하며 ng-app이 선언된 DOM 요소가 최상위 노드가 되어 여러 자식 $scope를 가지게 됩니다. 즉, DOM과 같은 계층적 구조에서 최상위 계층에 $rootScope가 존재하는 것입니다. 


   다음은 ng-controller를 계층적으로 가지는 예제입니다.


 * ajsarch.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="arch">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script type="text/javascript">
            var app = angular.module("arch", []);
            app.controller("parentCtrl"function($scope) {
                $scope.parent = {name"parent Moon"};    
            });
 
            app.controller("childCtrl"function($scope) {
                $scope.child = {name"child Moon"};
                $scope.changeParentName = function() {
                    $scope.parent.name = "Another Jack";
                };
            });
        </script>
    </head>
    <body ng-controller="parentCtrl">
        <h1>부모 이름: {{parent.name}}</h1>
        <div ng-controller="childCtrl" style="padding-left: 20px;">
            <h2>부모 이름: {{parent.name}}</h2>
            <h2>자식 이름: {{child.name}}</h2>
            <button ng-click="changeParentName()">부모 이름 변경</button>
        </div>
    </body>
</html>
cs







  4-2. Scope 타입

   지금까지 본 $scope 객체나 $rootScope 객체는 AngularJS 내부에서 정의하는 Scope 타입의 인스턴스입니다. 즉, 다음과 같이 별도의 생성자 함수가 AngularJS 내부에 정의되어 있습니다.


1
2
3
4
function Scope() { ... }
Scope.prototype.$apply = function () {};
Scope.prototype.$digest = function() {};
...
cs



   AJS는 초기 부트스트랩 시 프레임워크 내부에서 $rootScope를 new Scope()로 생성한 뒤 해당 $rootScope를 서비스로 제공합니다. 그리고 ng-controller나 웹 앱에서는 다음과 같이 $rootScope를 이용해서 자식 $scope 객체들을 만들 수 있습니다.


 

1
var $scope = $rootScope.$new();
cs



   서비스는 나중에 다루기로 하고, scope 타입의 프로토타입 메서드를 살펴보도록 합시다.


   $apply(표현식 또는 함수)

    주로 외부 환경에서 AJS 표현식을 실행할 때 사용


   $broadcast(이벤트 이름, 인자들...)

    첫 번째 인자인 이벤트 이름으로 하는 이벤트를 모든 하위 $scope에게 발생시킴


   $destroy() 

    현재 $scope를 제거, 모든 자식 $scope까지 제거


   $digest()

    $scope와 그 자식에 등록된 모든 $watch 리스너 함수를 실행. $watch 리스너 함수가 보는 표현식에 대해 변화가 없다면 리스너 함수는 실행시키지 않음


   $emit(이벤트명, 인자들...)

    해당 $scope를 기준으로 상위 계층 $scope에게 이벤트 명으로 인자를 전달. 물론 $on으로 이벤트 명을 듣고 있는 상위 계층에 한하여 전파


   $new(독립여부)

    새로운 $scope를 생성. 독립여부를 true, false 로 전달하는데 true일 경우 프로토타입을 기반으로 상속하지 않게 됨

 

   $on(이벤트 이름, 리스너 함수)

    주어진 이벤트 이름으로 이벤트를 감지하다가 해당 이벤트가 발생하면 리스너 함수 호출


   $watch(표현식, 리스너 함수, 동등성여부)

    대상 $scope에 특정 표현식을 감지하는 리스너 함수를 등록



   위 함수를 사용 시점별로 묶으면 데이터 바인딩 처리 시 $apply, $digest, $watch를 사용하고 사용자 정의 이벤트 처리 시 $broadcast, $emit, $on 을 사용합니다.


   $$new와 $destroy는 $scope의 생성과 파괴 처리 시 사용합니다. 컨트롤러에서 사용하는 $scope 객체는 scope 타입의 인스턴스이므로 프로토타입 상속에 의해 위 메서드를 사용할 수 있습니다.




  4-2. $scope에서 사용자 정의 이벤트 처리

   AJS는 웹 앱에 애플리케이션 이벤트를 정의하고 이런 이벤트 처리에 대한 일련의 메카니즘을 제공합니다. 이러한 사용자 정의 이벤트는 모두 $scope 객체를 통하여 처리되는데 $scope 객체에서 특정 이벤트를 발생시키면 이벤트를 발생한 $scope 객체의 자식이나 부모 $scope에서 해당 이벤트를 듣고 있다 처리할 수 있는 것 입니다.


   다음 예제를 통해서 직접 살펴보도록 합시다. 이 예제는 메시지를 입력하여 전송을 하면 공지가 되는 식의 예제입니다.




 * ajsevent.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
41
42
43
44
45
46
47
48
49
50
51
52
<!doctype html>
<html ng-app="event">
    <head>
        <meta charset="UTF-8"/>
        <style>
            .ng-scope {border: 1px solid red; padding: 5px;}
            .msg-list-area {margin: 10px; height: 400px; border: 1px solid black;}
        </style>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
        <script type="text/javascript">
            var app = angular.module("event", []);
            app.controller("mainCtrl"function($scope) {
                $scope.broadcast = function(noticeMsg) {
                    $scope.$broadcast("chat:noticeMsg", noticeMsg);
                    $scope.noticeMsg = "";
                };
            });
 
            app.controller("chatMsgListCtrl", ['$scope''$rootScope'function($scope, $rootScope) {
                $scope.msgList = [];
                $rootScope.$on("chat:newMsg"function(e, newMsg) {
                    $scope.msgList.push(newMsg);
                });
 
                $scope.$on("chat:noticeMsg"function(e, noticeMsg) {
                    $scope.msgList.push("[공ㅈㅣ] " + noticeMsg);
                });
            }]);
 
            app.controller("chatMsgInputCtrl"function($scope) {
                $scope.submit = function(newMsg) {
                    $scope.$emit("chat:newMsg", newMsg);
                    $scope.newMsg = "";
                };
            });
 
        </script>
    </head>
    <body ng-controller="mainCtrl">
        <input type="text" ng-model="noticeMsg">
        <input type="button" value="공지 전송" ng-click="broadcast(noticeMsg)">
        <div class="msg-list-area" ng-controller="chatMsgListCtrl">
            <ul>
                <li ng-repeat="msg in msgList track by $index">{{msg}}</li>
            </ul>
        </div>
        <div ng-controller="chatMsgInputCtrl">
            <input type="text" ng-model="newMsg">
            <input type="button" value="전송" ng-click="submit(newMsg)">
        </div>
    </body>
</html>
cs








  위 예제에는 3개의 컨트롤러 함수가 정의되었습니다. mainCtrl, chatMsgListCtrl, chatMsgInputCtrl 컨트롤러 함수가 정의되어 있습니다. 

  각 컨트롤러와 연결된 DOM에는 각 컨트롤러가 생성하는 $scope 객체가 연결됩니다. 



end






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

다른 카테고리의 글 목록

Web/AngularJS 카테고리의 포스트를 톺아봅니다
조회수 확인

템플릿 시스템과 바인딩




5. 조건적인 데이터 표현을 위한 템플릿

 템플릿을 작성하다보면 조건에 따라 다른 코드를 작성하고 싶을때가 있습니다. 자바스크립트에서는 이렇게 특정 조건일 때 특정 코드 블록을 지정하는 데 조건문을 이용합니다. 


 AngularJS의 템플릿에서도 자바스크립트의 switch와 if문과 비슷한 기능의 지시자를 제공합니다. ng-switch와 ng-if가 이에 해당합니다. 비슷한 지시자로 ng-show와 ng-hide 지시자가 있습니다. 이 두 지시자는 조건적으로 적용된 DOM의 CSS 속성 중 display 속성을 제어하는 지시자입니다. 


 다음은 ng-switch 사용법입니다.


<ANY ng-switch="표현식">

<ANY ng-switch-when="조건 일치 값1">...</ANY>

<ANY ng-switch-when="조건 일치 값2">...</ANY>

...

<ANY ng-switch-default>...</ANY>

</ANY>



 ng-switch 문은 어떠한 HTML 태그에서도 사용할 수 있으며 부모 요소와 자식 요소가 필요합니다. 부모 요소에서 ng-switch="표현식"을 속성으로 작성 할 수 있는데 표현식에는 $scope의 속성명이 오게 됩니다. 가령 ng-switch="value"라고 작성하면 $scope.value의 값이 조건으로 주어지는 것입니다. 이때 자식 요소에서는 ng-switch-when="조건 일치 값"을 작성할 수 있습니다. 가령 $scope.value의 값이 "matchValue1"이고 ng-switch-when="matchValue1"이라고 자식 요소에 작성되어 있으면 해당 자식 요소가 실제로 화면에 보이게 됩니다.


 다음은 상단에 있는 텍스트에 따라 아래에 있는 색을 선택하여 바꾸는 예제입니다.



 * ngexam.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>
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
        <style type="text/css">
            .box {
                width: 100px;
                height: 100px;
            }
            .red {
                background-color: red;
            }
            .green {
                background-color: green;
            }
            .blue {
                background-color: blue;
            }
            .black {
                background-color: black;
            }
        </style>
    </head>
    <body>
        <div>
            <input type="radio" ng-model="color" value="red">빨간색<br/>
            <input type="radio" ng-model="color" value="green">녹색<br/>
            <input type="radio" ng-model="color" value="blue">파란색<br/>
            
            <div ng-switch="color">
                <div ng-switch-when="red" class="box red"></div>
                <div ng-switch-when="green" class="box green"></div>
                <div ng-switch-when="blue" class="box blue"></div>
                <div ng-switch-default class="box black"></div>
            </div>
        </div>
    </body>
</html>
cs











 이제 ng-if 지시자를 살펴봅시다. 다음은 ng-if 지시자의 사용법입니다.


<ANY ng-if="표현식">

...

</ANY>




 ng-if는 표현식 결과값의 참/거짓 여부에 따라 해당 요소를 없애거나 생성하는 지시자입니다. 


 아래 예제를 통해서 ng-if를 알아봅시다.



 * ngifexam.html


1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html ng-app>
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
    </head>
    <body>
        약관에 동의: <input type="checkbox" ng-model="checked" ng-init="checked=false" />
        <br/>
        동의하면 다음으로 진행됩니다.
        <button ng-if="checked">다음</button>
    </body>
</html>
cs









6. 비지니스 로직을 처리하기 위한 템플릿 

 AngularJS에서는 템플릿의 특정 부분을 처리하는 자바스크립트 함수를 선언할 수 있습니다. 즉 화면 어느 한 부분의 애플리케이션 로직에 대한 코드를 특정 자바스크립트 함수에서 작성해야 합니다. 앞에서 본 것처럼 화면에서 데이터를 반복해서 출력하거나 조건에 따라 출력하는 코드는 모두 템플릿에 작성하면 됩니다. 


 하지만 데이터를 가공하거나 서버에 데이터를 저장하고 서버로부터 데이터를 불러오는 등의 애플리케이션 로직에 해당하는 코드는 자바스크립트로 작성해야 합니다. 그래서 템플릿의 특정 부분을 처리하고 자바스크립트 함수 이름을 템플릿에 명시하게 합니다.


 이러한 함수를 컨트롤러 함수라 하고 ng-controller 지시자를 이용해 템플릿이 컨트롤러 함수를 참조할 수 있게 해줍니다.


 템플릿에서 컨트롤러 함수를 사용하려면 ng-controller 지시자를 특정 요소의 속성으로 작성하면 됩니다. 그러면 해당 요소와 자식 요소를 모두 포함하여 컨트롤할 대상 함수를 지정하게 되고 대상 컨트롤러 함수에서 애플리케이션의 로직을 구현할 수 있습니다. 다음은 ng-controller의 사용법입니다.



<ANY ng-controller="표현식">

...

<ANY>


 여기서 표현식은 전역적(Global)으로 접근할 수 있는 자바스크립트 함수의 이름이거나 모듈로 등록된 컨트롤러 함수 이름이거나 현재 scope에서 접근할 수 있는 함수의 이름입니다.


 다음 예제는 고객 목록 중에서 18세 미만인 고객을 별도로 보여주는 예제입니다.


 * ngctrlexam.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
41
42
43
<!doctype html>
<html ng-app="ctrl">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
        <script>
            var app = angular.module("ctrl", []);
            app.controller("customerCtrl"function($scope) {
                var customerList = [
                    {name'Jane', age: 7}, {name'Jolie', age: 21}
                ];
                var youngCustomerList = [];
                
                angular.forEach(customerList, function(value, key) {
                    if (value.age < 18) {
                        youngCustomerList.push(value);
                    }
                });
                
                $scope.customerList = customerList;
                $scope.youngCustomerList = youngCustomerList;
            });
        </script>
    </head>
    <body ng-controller="customerCtrl">
        <div>
            고객목록
            <ul>
                <li ng-repeat="customer in customerList">
                    [{{$index + 1}}] 고객이름: {{customer.name}}, 고객나이: {{customer.age}}
                </li>
            </ul>
        </div>
        <div>
            18세 미만 고객
            <ul>
                <li ng-repeat="youngCustomer in youngCustomerList">
                    [{{$index + 1}}] 고객 이름: {{youngCustomer.name}}, 고객 나이: {{youngCustomer.age}}
                </li>
            </ul>
        </div>
    </body>
</html>
cs







 예제 코드를 보면 customerCtrl 컨트롤러 함수가 정의되어 있습니다. 해당 컨트롤러 함수에서는 고객 목록이 정의되어 있으며, 고객 목록 중 나이가 18세 미만인 고객만 별도의 고객으로 추려내고 있습니다.


 그리고 해당 고객 목록을 인자로 받은 $scope에 대입하고 있습니다. 이렇게 $scope에 속성으로 대입해야 컨트롤러 함수와 연결된 템플릿의 요소에서 표현식을 이용해 접근할 수 있습니다. 




7. 폼과 유효성 검사를 위한 템플릿(폼/입력 지시자)

 사용자와 서버가 데이터를 주고받기 위해 HTMl에서는 <form> 요소를 제공합니다. 이 <form> 요소에는 <input>, <select>, <textarea> 요소를 자식 요소로 사용할 수 있습니다. 하지만 폼 양식을 서버로 제출하기 전에 사용자가 입력한 값을 검증해야 할 때가 종종 있습니다. 


 AngularJS는 유효성 검증을 손 쉽게 처리할 수 있게 폼 양식 유효성 검증과 관련된 지시자를 제공합니다. 그럼 <form> 요소와 <input>, <select>, <textarea> 요소에서 사용할 수 있는 지시자를 살펴보도록 하겠습니다. 


 텍스트 타입 사용법


<input type="text" ng-model="문자열" name="문자열" ng-required="문자열" ng-minlength="숫자" ng-maxlength="숫자" ng-pattern="문자열" ng-change="문자열"



 - ng-model: 바인딩 대상이 되는 모델명

 - name: 폼에서 사용하는 이름

 - ng-required: 필수 입력 여부

 - ng-minlength: 최소 글자 수

 - ng-maxlength: 최대 글자 수

 - ng-pattern: 입력된 값과 비교될 정규 표현식이며 /정규표현식/과 같은 값이 요구됩니다.

 - ng-change: 사용자의 입력이 발생할 때 실행될 표현식



 다음 예제는 이름과 핸드폰 번호를 입력하는 <input> 요소의 유효성을 검증하여 그 결과를 보여주는 예제입니다.


 

 * ngvalidexam.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>
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
    </head>
    <body>
        <form name="sampleFrm" ng-init="name = 'Black Jack'">
            이름: <input type="text" name="name" ng-model="name" ng-maxlength="3" ng-required="true"/>
            <span class="error" ng-show="sampleFrm.name.$error.required">필수 입력</span>
            <br/>
            핸드폰 번호: <input type="text" name="tel" ng-model="tel" ng-pattern="/^\d{3}-\d{3,4}-\d{4}$/">
            <span class="error" ng-show="sampleFrm.tel.$error.pattern">000-0000-0000</span>
            <br/>
            <p>사용자 정보: {{name}}/{{tel}}</p>
            <p>sampleFrm.name.$valid = {{sampleFrm.name.$valid}}</p>
            <p>sampleFrm.name.$error = {{sampleFrm.name.$error}}</p>
            <p>sampleFrm.tel.$valid = {{sampleFrm.tel.$valid}}</p>
            <p>sampleFrm.tel.$error = {{sampleFrm.tel.$error}}</p>
            <p>sampleFrm.$valid = {{sampleFrm.$valid}}</p>
            <p>sampleFrm.$error.required = {{!!sampleFrm.$error.required}}</p>
        </form>
    </body>
</html>
cs









 체크박스 타입 사용법


<input type="checkbox" ng-model="문자열" name="문자열" ng-true-value="문자열" ng-false-value="문자열" ng-required="true/false" ng-change="문자열">


 - ng-model: 바인딩 대상이 되는 모델명

 - name: 폼에서 사용하는 이름

 - ng-true-value: 체크박스를 체크했을 때 바인딩된 모델에 대입할 값

 - ng-false-value: 체크박스를 해제했을 때 바인딩된 모델에 대입할 값

 - ng-required: 필수 입력 여부

 - ng-change: 사용자의 입력이 발생할 때 실행될 표현식



 
 목록 요소 사용법
  
<select ng-model="문자열" name="문자열" ng-options="별도의 표현식" ng-required="true/false" >


 - ng-model: 바인딩 대상이 되는 모델명

 - name: 폼에서 사용할 이름

 - ng-required: 필수 입력 여부

 - ng-options: 옵션을 나타내기 위한 별도의 표현식



 AngularJS에서 제공하는 ng-options 지시자는 위에서 본 바와 같이 ng-repeat처럼 반복적인 데이터를 위한 별도의 표현식을 제공합니다. ~ for ~ in이 기본 구조이고 as 혹은 group by를 같이 사용합니다. 다음은 ng-options 지시자에 사용하는 표현식을 구성하는 요소에 관한 설명입니다.


  - array/object: $scope에 있는 배열 또는 객체에 접근하는 표현식

  - value: 배열 요소를 가리키는 내부변수 생성 표현식

  - label: <option> 요소의 라벨이 될 표현식(예: value.propertyName)

  - select: 부모인 <select> 요소에 모델로 바인딩되는 표현식

  - group: <optgroup> 요소가 되는 표현식





 * ngselectexam.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
41
42
43
44
<!doctype html>
<html ng-app>
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
    </head>
    <body>
        <div ng-init="countryList = [{name:'인천', code: 'ICN', continent: 'Asia'},
            {name: '도쿄', code: 'HND', continent: 'Asia'}, 
            {name: '미국', code: 'en', continent: 'Namerica'}]">
            <form name="myRouteFrm">
                <div>
                    출발 국가:
                    <select ng-model="depCountry" 
                        ng-options="country.name for country in countryList" ng-required="true">
                        <option value="">선택하여 주세요</option>
                    </select>
                </div>
                <div>
                    경유 국가:
                    <select ng-model="viaCountry" 
                        ng-options="country.name as country.name for country in countryList">
                        <option value="">선택하여 주세요</option>
                    </select>
                </div>
                <div>
                    도착 국가(대륙 그룹별):
                    <select ng-model="arrCountry" ng-options="country.name group by 
                        country.continent for country in countryList" ng-required="true">
                        <option value="">선택하여 주세요</option>
                    </select>
                </div>
            </form>
            <div>
                <p>출발 국가: {{depCountry.name}} </p>
                <p>경유 국가: {{viaCountry}} </p>
                <p>도착 국가: {{arrCountry.name}} </p>
            </div>
            <div ng-show="myRouteFrm.$invalid">
                출발 국가와 도착 국가는 필수로 선택하여 주세요.
            </div>
        </div>
    </body>
</html>
cs







 CSS 클래스로 유효성 검증 결과 표현하기

 AngularJS는 컨트롤 요소의 유효성 검증 결과를 요소의 CSS 클래스로 알아서 추가해 줍니다. 가령 특정 텍스트 타입의 입력 요소가 필수 입력 요소인데 해당 입력 요소에 값이 주어지지 않으면 AngularJS는 ng-invalid CSS 클래스를 해당 입력 요소에 추가합니다. 그런데 값이 입력되면 ng-invalid CSS 클래스는 없어지고 ng-valid CSS 클래스가 추가됩니다.


 이처럼 ng-required나 ng-pattern과 같은 유효성 검사를 위한 속성이 <input> 요소와 같은 컨트롤 요소에 사용되고 유효성 검증에 성공하면 ng-valid CSS 클래스가 실패하면 ng-invalid CSS 클래스가 자동으로 추가되는 것입니다.


 그 뿐만 아니라 해당 입력 요소에 사용자 입력이 없으면 ng-pristine CSS 클래스가 추가되고 사용자 입력이 발생하면 ng-dirty CSS 클래스가 추가됩니다.


 다음은 앞서 작성한 이름, 핸드폰 번호 유효성 검사에 대한 예제를 다시 작성해보겠습니다.



 * cssvalidexam.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
<!doctype html>
<html ng-app>
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
        <style type="text/css">
            .ng-invalid {
                border: 3px solid red;
            }
            .ng-valid {
                border: 3px solid green;
            }
            .ng-pristine {
                border-style: solid;
            }
            .ng-dirty {
                border-style: dashed;
            }
        </style>
    </head>
    <body>
        <form name="sampleFrm" ng-init="name = '철구'">
            이름: <input type="text" name="name" ng-model="name" ng-maxlength="3" ng-required="true">
            핸드폰 번호: <input type="text" name="tel" ng-model="tel" ng-pattern="/^\d{3}-\d{3,4}-\d{4}$/">
        </form>
    </body>
</html>
cs








 이벤트 처리를 위한 템플릿

  AngularJS는 HTML 요소에서 발생하는 이벤트에 대한 처리를 자바스크립트를 사용하지 않고 할 수 있게 해줍니다. 가령 특정 요소를 클릭하면 발생하는 클릭 이벤트를 ng-click 지시자를 이요해 이벤트 처리를 할 수 있습니다. 다음은 jQuery를 사용해 특정 버튼을 클릭했을 때 이벤트 처리 코드입니다.



1
2
3
4
5
<div id="button1" class="btn">Click</div>
 
$('div#button1').click(function() {
    clickCount++;
});
cs




  다음은 AngularJS의 ng-click 지시자를 사용한 코드입니다.


1
2
<div class="btn" ng-click="clickCount++">
 
cs




  사용자 클릭 이벤트 외에도 마우스오버, 마우스엔터, 키프레스 등 다양한 이벤트를 지원합니다. 







  다음은 이벤트 지시자의 사용 방법입니다.


<ANY ng-이벤트명="표현식">

...

</ANY>



  다음은 다양한 이벤트 처리를 보여주는 예제를 이벤트 지시자를 이용해서 구현한 예제 코드입니다.



 * eventexam.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
41
42
43
 
<!doctype html>
<html ng-app="event">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
        <style type="text/css">
            .box {
                width: 100px;
                height: 60px;
                margin: 10px;
                background-color: black;
                color: white;
                text-align: center;
                padding-top: 40px;
            }
        </style>
        <script>
            var app = angular.module("event", []);
            app.controller('mainCtrl'function($scope) {
                $scope.message = "";
                $scope.eventCnt = 0;
                $scope.handleEvt = function(message) {
                    $scope.message = message;
                    $scope.eventCnt++;
                }
            });
        </script>
    </head>
    <body ng-controller="mainCtrl">
        <div>
            <div class="box" ng-click="handleEvt('박스 클릭')">click</div>
            <div class="box" ng-mousedown="handleEvt('박스 mousedown 이벤트')">mousedown</div>
            <div class="box" ng-mouseenter="handleEvt('박스 mouseenter 이벤트')">mouseenter</div>
            <div class="box" ng-mousemove="handleEvt('박스 mousemove 이벤트')">mousemove</div>
            change: <input type="text" ng-model="inputText" ng-change="handleEvt('입력 박스의 값 변경')">
            keydown: <input type="text" ng-model="inputText2" ng-keydown="handleEvt($event.keyCode + '키코드 눌러짐')">
        </div>
        <div>
            <p>{{message}} {{eventCnt}}</p>
        </div>
    </body>
</html>
cs








8. CSS 클래스/스타일을 동적으로 처리하기 위한 템플릿

 웹 애플리케이션을 개발하다 보면 특정 상황에서 CSS 클래스를 동적으로 처리해야 할 때가 잦습니다. 가령 아이템 목록 중 특정 아이템에 highlight라는 CSS 클래스를 추가해 다른 아이템과 차이를 줄때가 그럴 것입니다. jQuery를 사용한다면 다음과 같이 <li> 요소를 선택하고 jQuery addClass 메서드를 이용해 자바스크립트에서 다음과 같이 처리했을 겁니다.



1
2
3
4
5
if (name === "Jack") {
    $('li').addClass('highlight');
else {
    $('li').removeClass('hightlight');
}
cs




 하지만 AngularJS에서는 ng-class 지시자를 이용해 템플릿에서 CSS 클래스를 동적으로 처리할 수 있습니다.


<ANY ng-class="표현식">

...

</ANY>

또는

<ANY class="ng-class: 표현식;">

...

</ANY>


 ng-class는 앞에서 본 바와 같이 태그 속성 또는 class 속성의 값으로 ng-class: 표현식과 같이 사용할 수 있습니다. 표현식의 결과 값은 CSS 클래스 이름을 스페이스로 구분한 문자열이거나, CSS 클래스 이름들로 구성된 배열이거나, CSS 클래스 이름이 속성이름이고 true/false를 값으로 하는 객체여야 합니다.


 객체일 경우 특정 CSS 클래스 이름의 값이 true 일 때 해당 CSS 클래스가 요소에 적용됩니다. 예제를 통해서 과일 이름을 나열한 후 과일과 관련된 CSS를 추가후, 짝수 목록에 파란색을 나타내는 예제입니다.



 * cssdynaexam.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>
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
        <style type="text/css">
            .apple {background-color: red; }
            .lemon {background-color: yellow; }
            .even {background-color: blue; }
        </style>
    </head>
    <body>
        <div ng-init="fruitList = ['apple', 'banana', 'tomato', 'lemon', 'grape', 'strawberry', 'blueberry']">
            <ul>
                <li ng-repeat="fruit in fruitList" ng-class="'{{fruit}}'">{{fruit}}</li>
            </ul>
            <ul>
                <li ng-repeat="fruit in fruitList" ng-class="{'even': {{$index % 2 == 0 }}}">{{fruit}}</li>
            </ul>
        </div>
    </body>
</html>
cs










 앞에서 본 ng-class 지시자는 사전에 CSS 클래스가 스타일 시트에 정의되어 있고 해당 CSS 클래스를 HTML 요소에서 조건적으로 적용할 때 사용했습니다. 하지만 만약 CSS 클래스가 적용되어 있지 않고 HTML 요소의 스타일을 동적으로 변경하고 싶을 때는 어떻게 할 수 있을까요?


 이러한 방법을 AngularJS를 통해서 작성하면 아래와 같습니다.


<ANY ng-style="표현식">

...

</ANY>



 표현식의 계산 결과에 따라 HTML 요소에 CSS 스타일을 적용합니다. 표현식의 결과는 객체여야 하고 해당 객체의 키가 CSS 스타일의 이름이 되며 객체의 값이 CSS 스타일 이름에 대한 값이 됩니다.



 * cssdynaexam2.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="dyna">
    <head>
        <meta charset="UTF-8"/>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
        <script>
            var app = angular.module("dyna", []);
            app.controller('mainCtrl'function($scope) {
                $scope.bgStyle = {
                    backgroundColor: 'red'
                };
                $scope.changeColor = function(color) {
                    $scope.bgStyle.backgroundColor = color;
                };
            });
        </script>
    </head>
    <body ng-controller="mainCtrl">
        <div>
            <div ng-style="bgStyle">{{bgStyle.backgroundColor}}</div>
            <button ng-click="changeColor('blue')">색 변경</button>
        </div>
    </body>
</html>
cs










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

다른 카테고리의 글 목록

Web/AngularJS 카테고리의 포스트를 톺아봅니다
조회수 확인

템플릿 시스템과 데이터 바인딩






구조적이고 재사용하기 좋은 웹 어플리케이션을 개발하려면 화면과 데이터의 분리가 필수적입니다. 나아가 데이터와 화면 사이의 싱크도 필요합니다. 기존 JS 기반의 웹 어플리케이션 대부분이 데이터와 화면 사이가 끈끈하게 이어져 있고 데이터와 화면 사이의 싱크를 위해서 반복적인 코드를 사용했습니다.


그렇게 하다보니 스파게티 코드(Spaghetti Code)라 하여 화면 처리 코드와 데이터 처리 코드가 서로 엉켜있어 유지보수하기 어렵고 재사용 불가능한 어플리케이션을 만들게 됩니다.


하지만 AngularJS는 화면과 데이터의 분리를 용이하게 하는 템플릿 시스템데이터와 화면 사이를 싱크할 수 있게 하는 데이터 바인딩을 제공함으로써 앞선 문제점을 말끔하게 해결해 줍니다. 




1. 템플릿의 이해

 브라우저에서 사용자가 보는 화면을 그리려면 HTML 문서를 작성해야 합니다. 작성한 HTML 문서를 웹 서버에 두면 사용자의 웹 브라우저가 URL을 받아 해당 웹 서버로 요청을 보내고, 웹 서버는 응답으로서 요청한 HTML 문서를 보내줍니다. 웹 어플리케이션도 마찬가지로 HTML 문서를 작성해야 합니다.


 하지만 한 번 작성하면 오랫동안 바뀌지 않는 웹 페이지와는 다르게 브라우저의 요청이 있을 때마다 주어진 데이터에 따라 내용이 바뀌어야 합니다. 그래서 지금까지 이러한 일을 웹 템플릿 시스템을 이용해서 해결했고, 주로 서버 측에서 하곤 했습니다.


 친숙한 서버 측 동적 페이지 예로 JSP(JavaServer Pages) 가 있습니다. 다음은 JSP의 템플릿 코드입니다. ${...}으로 JSP에서 자바의 데이터에 접근할 수 있습니다.



1
2
3
4
5
6
<html>
    <head>...</head>
    <body>
        <h1>Hello ${name}</h1>
    </body>
</html>
cs




 최근에는 서버에서 데이터와 템플릿을 조합해 요청마다 전체 페이지를 브라우저로 보내주기보다는 JSON 포맷의 데이터나 HTML 페이지의 일부 조각만 서버가 보내주고 클라이언트에서 이를 동적으로 처리하는 AJAX 기술을 많이 사용해 왔습니다. AJAX를 이용하면 사용자의 입력에 즉각적으로 반응하여 화면을 동적으로 처리할 수 있기 때문에 UI/UX적으로 뛰어난 웹 어플리케이션을 개발할 수 있기 때문입니다.


 다음은 jQuery를 이용해서 구현한 간단한 예제입니다.




 * usingjquery.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>
    <head>
        <meta charset="UTF-8"/>
        <title>jquery dom handling</title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
        <script>
            $(function() {
                var name="Yeonsu";
                var favorite = ["Watermelon""Orange""Strawberry"];
                
                $("#greeting").text("hello " + name);
                for (var i = favorite.length - 1; i >= 0; i--) {
                    $("#favorite").append("<li><a href='#" + favorite[i] + "'>" + favorite[i] + "</a></li>");
                }
            });
        </script>
    </head>
    <body>
        <h1 id="greeting"></h1>
        <h2>좋아하는 과일</h2>
        <ul id="favorite"></ul>
    </body>
</html>
cs




 








 위 코드를 보면 name과 favorite이라는 변수가 있고, jQuery를 이용해 이러한 데이터 기반의 DOM을 생성하고 추가했습니다. 하지만 위와 같은 방식으로 어플리케이션을 개발하면서 유지보수와 확장성에 대한 문제점이 나오곤 했습니다. 위 코드를 보면 HTML의 구조를 자바스크립트가 잘 알고 있습니다. 가령 favorite이라는 id를 가진 <ul> 태그가 있어야 하며 <ul> 태그에 들어갈 <li> 태그도 자바스크립트에서 직접 정의하고 있습니다. 어플리케이션의 크기가 커질수록 자바스크립트에서 HTML 태그를 작성하는 코드가 늘고 DOM을 처리하는 복잡도가 증가할 것 입니다.


 이제 이 정도로 템플릿을 언급하고 본격적으로 AngularJS의 템플릿을 적용해보도록 하겠습니다. 




2. AngularJS 템플릿

 AngularJS에서는 템플릿이 HTML 그 자체입니다. AngularJS는 DOM 자체를 템플릿으로 사용합니다. 이 DOM은 HTML, CSS 그리고 AngularJS가 제공하는 특정한 요소나 속성인 지시자가 포함됩니다. 쉽게 이해할 수 있게 앞에서 작성한 예제를 AngularJS로 작성해봤습니다.



 * angulartem.html


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE HTML>
<html ng-app>
    <head>
        <meta charset="UTF-8"/>
        <title>angularJS Template</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
    </head>
    <body ng-init="person = {name: 'yeonsu', favorite : ['수박', '귤', '사과']}">
        <h1> hello {{person.name}}</h1>
        <h2> 좋아하는 과일</h2>
        <ul ng-repeat='fruit in person.favorite'>
            <li><a href="#{{fruit}}">{{fruit}}</a></li>
        </ul>
    </body>
</html>
cs







 위 코드를 보면 자바스크립트 코드를 단 한 줄도 작성하지 않음을 알 수 있습니다. 그리고 ng로 시작하는 속성과 {{}}을 제외하고는 모두 HTML 코드입니다. 


 AngularJS는 DOM 자체 템플릿이므로 일일이 자바스크립트로 render와 같은 함수를 호출할 필요도 없고 그 결과를 특정 DOM에 삽입할 필요도 없습니다.


 다음은 템플릿 작성시 사용되는 AngularJS의 기능입니다.



 - 지시자: 기본 HTML을 확장하거나 새로 추가한 요소나 속성. ng-repeat, ng-app 등이 지시자에 해당. 또한 사용자가 만든 재사용할 수 있는 위젯과 같은 지시자도 해당

 - 마크업: 기본 HTML 마크업과 이중 중괄호 - {{ 표현식 }}

 - 필터: 사용자에게 보여주는 데이터의 형식을 필터로 처리

 - 폼 컨트롤: 입력 상자의 유효성 검사를 위해 AngularJS가 폼 태그를 확장. 폼 태그를 사용하게 되면 자동으로 폼 컨트롤을 사용.





 2-1. 이중 중괄호와 AngularJS 표현식

  템플릿에서 이중 중괄호('{{}}')를 사용해 특정 위치에 표현할 데이터를 지정했습니다. 앞의 예제에서 다음과 같은 코드를 보았습니다.



1
<h1>hello {{person.name}}</h1>
cs




  위 코드를 해석하면 "hello라는 문자열 옆에 person 객체의 name 속성을 구해서 h1의 크기로 화면에 출력"이라고 해석할 수 있습니다. 그렇습니다. 이중 중괄호 내에 AngularJS 표현식을 작성 할 수 있습니다. 


  AngularJS 표현식은 자바스크립트 표현식과 비슷합니다. 아래 목록은 AngularJS 표현식과 자바스크립트 표현식의 차이점입니다.

  

  - 객체의 접근: 기본적으로 자바스크립트는 모든 객체를 최상위 객체인 window 객체안에서 찾습니다. 이와 반대로 AngularJS는 표현식에 사용된 객체를 scope 안에서 찾습니다. 가령 {{ user }} 라고 작성했다면 user 객체에 접근하는데 window.user가 아닌 scope.user로 scope 객체에 접근합니다. 


  - undefined와 null 무시: 자바스크립트 표현식으로 objectA.propertyA라고 작성했다고 가정합니다. 만약 objectA가  선언되지 않으면 objectA는 undefined일 것입니다. Undefined에서 propertyA에 접근하려 했으니 자바스크립트에서는 오류를 발생합니다. 하지만 AngularJS에서는 오류를 발생하지 않고 무시합니다. 즉 템플릿 {{objectA.propertyA}}라고 작성하여 접근 시에 propertyA가 선언되어 있지 않더라도 아무런 오류를 발생하지 않습니다. 


  - 제어문 작성이 안 됨: if문과 같은 조건절이나 for문과 같은 반복문 그리고 throw문은 작성할 수 없습니다.


  - 필터: AngularJS의 필터를 표현식에서 사용할 수 있습니다. 필터는 파이프(|)를 이용해서 표현식에서 사용할 수 있습니다. 가령 money가 있고 2000이라는 값이 할당되었을 때, 이때 2000을 2,000원 혹은 $2,000 으로 표현하고 싶을 때 우리는 currency 필터를 사용할 수 있습니다. 템플릿에서 {{ money | currency }} 와 같은 방식으로 작성하면 됩니다. AngularJS에서는 몇 가지 필터를 미리 만들어서 제공하고 있습니다. 


 

  아래는 위의 몇 가지 표현식을 실행해보는 예제입니다.


 * angularexp.html


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE HTML>
<html ng-app>
    <head>
        <meta charset="UTF-8"/>
        <title>angularJS template</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
    </head>
    <body ng-init="person = {name: 'yeonsu', favorite: ['사과', '귤', '감']}">
        <h1>hello {{person.name}}</h1>
        <p> 좋아하는 과일의 갯수: {{numberOfFavorite = person.favorite.length}}</p>
        <p>과일 갯수 * $100 = {{numberOfFavorite * 100 | currency}}</p>
        <p>yeonsu가 맞습니까? {{person.name == 'yeonsu'}}</p>
        <p>좋아하는 과일 수가 4개보다 많습니까? {{numberOfFavorite > 4 }}</p>
        <p>scope에 없는 객체 접근 하면? {{car.type.name}}</p>
    </body>
</html>
cs











3. 데이터 바인딩의 이해

 자바스크립트 웹 앱의 복잡도가 증가하면서 브라우저의 메모리에 있는 여러 개의 자바스크립트 객체와 화면에 있는 데이터를 일치시키기가 매우 어려워졌습니다. 이러한 어려운 작업을 쉽게 해주는 해결책이 있다면 그건 데이터 바인딩일 것입니다. 


 데이터 바인딩이란 두 데이터 혹은 정보의 소스를 모두 일치시키는 기법입니다. 즉 화면에 보이는 데이터와 브라우저 메모리에 있는 데이터를 일치시키는 기법입니다. 많은 자바스크립트 프레임워크가 이러한 데이터 바인딩 기술을 제공하고 있습니다. 하지만 대다수의 자바스크립트 프레임워크가 단방향 데이터 바인딩을 지원하는 반면 AngularJS는 양방향 데이터 바인딩을 제공합니다. 다음은 단방향 데이터 바인딩과 양방향 데이터 바인딩의 차이점을 설명한 그림입니다.










 단방향 데이터 바인딩은 데이터와 템플릿을 결합하여 화면을 생성합니다. 반면 양방향 데이터 바인딩은 데이터의 변화를 감지해 템플릿과 결합하여 화면을 갱신하고 화면에서의 입력에 따라 데이터를 갱신합니다. 즉, 데이터와 화면 사이의 데이터가 계속해서 일치되는 것입니다.


 다음은 단방향 데이터 바인딩 예제입니다.



 * mustache.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
 <!doctype html>
<html>
    <head>
        <meta charset="UTF-8"/>
        <title>one way data binding</title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.2.1/mustache.js"></script>
        <script>
            $(function() {
                // 
                var menuList = [
                    {itemId: 1, itemName: '베이글', itemPrice: 3000, itemCount: 0},
                    {itemId: 2, itemName: '소이 라떼', itemPrice: 4000, itemCount: 0},
                    {itemId: 3, itemName: '카라멜 마끼아또', itemPrice: 4500, itemCount: 0}
                ];
                
                // 
                var menuListTpl = $("#menuListTpl").html();
                var invoiceTpl = $("#invoiceTpl").html();
                
                // 
                var menuListHtml = Mustache.render(menuListTpl, menuList);
                var invoiceHtml = Mustache.render(invoiceTpl, {totalPrice: 0});
                
                //
                var invoiceEl = $("#invoice").html(invoiceHtml);
                
                $("#menu-list").html(menuListHtml);
                
                // 
                $("#addContract").click(function() {
                    var totalPrice = 0;
                    for (var i = menuList.length - 1; i >= 0; i--) {
                        $itemEl = $("#item-id-" + menuList[i].itemId);
                        var price = menuList[i].itemPrice;
                        var count = $itemEl.find(".item-count").val();
                        
                        totalPrice = totalPrice + (price * Number(count));
                    };
                    
                    //
                    invoiceEl.html(Mustache.render(invoiceTpl, {totalPrice: totalPrice}));
                });
            });
        </script>
    </head>
    <body>
        <div>
            <h1>메뉴판</h1>
            <h2>메뉴 목록</h2>
            <div id="menu-list">
            </div>
            <button id="addContract">구매</button>
            <h2>구입 가격</h2>
            <div id="invoice">
            </div>
        </div>
        
        <script type="text/template" id="menuListTpl">
            <table>
                <thead>
                    <tr><th>메뉴</th><th>가격</th><th>갯수</th></tr>
                </thead>
                <tbody>
                    {{#.}}
                    <tr id="item-id-{{itemId}}">
                        <td class="item-name">
                            {{itemName}}
                        </td>
                        <td class="item-price">
                            {{itemPrice}}
                        </td>
                        <td>
                            <input class="item-count" type="text" value="{{itemCount}}">
                        </td>
                    </tr>
                    {{/.}}
                </tbody>
            </table>
        </script>
        <script type="text/template" id="invoiceTpl">
            가격: {{totalPrice}}
        </script>
    </body>
</html>
cs






 다음은 위 예제를 양방향 데이터 바인딩으로 변경해 보겠습니다.



 * angularway.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE HTML>
<html ng-app="way">
    <head>
        <meta charset="UTF-8"/>
        <title>two way data binding</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
        <script>
            var app = angular.module("way", []);
            app.controller("mainCtrl"function($scope) {
                var menuList = [
                    {itemId: 1, itemName: '베이글', itemPrice: 3000, itemCount: 0},
                    {itemId: 2, itemName: '소이 라떼', itemPrice: 4000, itemCount: 0},
                    {itemId: 3, itemName: '카라멜 마끼아또', itemPrice: 4500, itemCount: 0}
                ];
 
                $scope.menuList = menuList;
                $scope.totalPrice = 0;
                
                $scope.buy = function(){
                    $scope.totalPrice = 0;
                    
                    angular.forEach($scope.menuList, function(menu, idx) {
                        $scope.totalPrice = $scope.totalPrice + (menu.itemPrice * Number(menu.itemCount));
                    });
                };
            });
        </script>
    </head>
    <body ng-controller="mainCtrl">
        <div>
            <h1>메뉴판</h1>
            <h2>메뉴 목록</h2>
            <table>
                <thead>
                    <tr>
                        <th>메뉴</th>
                        <th>가격</th>
                        <th>갯수</th>
                    </tr>
                </thead>
                <tbody>
                    <tr ng-repeat="menu in menuList">
                        <td>{{menu.itemName}}</td>
                        <td>{{menu.itemPrice}}</td>
                        <td><input type="text" ng-model="menu.itemCount"></td>
                    </tr>
                </tbody>
            </table>
            <button ng-click="buy()">구매</button>
            <h2>구입 가격</h2>
            <div>
                가격: {{totalPrice}}
            </div>
        </div>
    </body>
</html>
cs











  이전 코드와 비교해 보면 훨씬 더 간결하고 읽기 쉬워졌습니다. 더욱 중요한 건 버튼 클릭 이벤트를 처리하는 코드와 화면 갱신하는 자바스크립트 코드가 빠졌다는 것입니다. 






4. 반복적인 데이터 표현을 위한 템플릿(반복 지시자)

 웹 앱에서 특정한 형식이 있는 데이터를 반복해서 표현하는 일은 가장 흔한 데이터 표현일 것입니다. 가령 구매 목록이나 연락처 목록들이 그러합니다. 반복적으로 표현할 데이터는 주로 배열에 들어 있는데 AngularJS에서는 반복적인 데이터 표현을 위해서 ng-repeat 지시자를 제공합니다. 다음은 ng-repeat의 사용법입니다.


 - <any ng-repeat="변수명 in 표현식">

  변수명은 주어진 배열의 요소를 반복문 내부에서 참조할 때 사용됩니다. 표현식은 $scope 내의 배열과 같은 순환할 대상을 가리킵니다.


  - <any ng-repeat="(key 변수명, value 변수명) in 표현식">

  자바스크립트 객체같은 데이터를 순환할 때 사용합니다. key 변수명은 반복문 내부에서 객체의 key를 참조할 변수명이고 value 변수명은 마찬가지로 참조하는 value의 변수명입니다.


  - <any ng-repeat="변수명 in 표현식 track by 표현식" >

  배열 요소와 생성되는 DOM 요소를 연결할 때 사용하는 고유한 값을 지정할 수 있습니다. Track by를 별도로 작성하지 않으면 AngularJS는 동일하지 않은 값에 $$hashKey 속성을 추가하여 DOM 요소와 연결할 때 사용합니다. 그리고 var items =[1,1]; 과 같은 동일한 값을 ng-repeat으로 표현하려고 하면 item in item track by $index로 $index에 의해 DOM 요소와 연결되게 해야 합니다. Track by를 이용해 고유한 속성값을 지정하면 무의미한 DOM 랜더링을 막을 수 있습니다. Track by를 지정하지 않으면 매번 다른 $$hashKey를 생성하므로 그 때마다 매번 DOM을 변경하기 때문입니다.




  ng-repeat을 적용한 HTML 요소는 배열 요소의 개수만큼 HTML 요소를 생성합니다. 그리고 해당 HTML 요소에는 별도의 scope 영역이 생성되는데 해당 scope 영역에서만 사용할 수 있는 특별한 속성을 제공합니다.



  다음은 고객 목록과 친구목록을 보여주는 예제로 ng-repeat의 각 사용법을 보도록 하겠습니다.



 * repeat.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
<!doctype html>
<html ng-app>
    <head>
        <meta charset="UTF-8"/>
        <title>ng-repeat exam</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
    </head>
    <body>
        <div ng-init="customerList = [{name:'Jack', age:31}, {name:'Jolie', age:25}]">
            고객 목록
            <ul>
                <li ng-repeat="customer in customerList">
                    [{{$index + 1}}] 고객 이름: {{customer.name}}, 고객 나이: {{customer.age}}
                </li>
            </ul>
        </div>
        <div ng-init="friendList = {name:'Paul', age: 22, hobby:'Game'}">
            내 친구 소개
            <ul>
                <li ng-repeat="(attr, value) in friendList">
                    <p>{{attr}} : {{value}} </p>
                </li>
            </ul>
        </div>
    </body>
</html>
cs


 








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

다른 카테고리의 글 목록

Web/AngularJS 카테고리의 포스트를 톺아봅니다