Web 검색 결과

244개 발견
  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.03.25 - Palpit

    [jQuery] jQ Mobile 그리드(Grids) - jQuery Mobile 강좌

  8. 미리보기
    2016.03.25 - Palpit

    [jQuery] jQ Mobile 테이블(Tables) - jQuery Mobile 강좌

조회수 확인

필터(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 카테고리의 포스트를 톺아봅니다
조회수 확인

W3School 를 참고하여 작성하는 jQuery Mobile 강좌입니다.


아래 링크들은 Tutorial 편입니다.


jQ Mobile 시작하기

jQ Mobile 페이지(Pages)

jQ Mobile 버튼(Buttons)

jQ Mobile 아이콘(Icons)

jQ Mobile 팝업(Popups)

jQ Mobile 툴바(Toolbars)

jQ Mobile 네비바(Navibars)

jQ Mobile 패널(Panels)

jQ Mobile 접는 블록(Collapsibles)

jQ Mobile 테이블(Tables)

jQ Mobile 그리드(Grids)




jQuery Mobile Grids

 jQuery Mobile Layout Grids

  jQuery Mobile은 일련의 CSS 기반의 컬럼 레이아웃을 제공합니다. 그러나, 컬럼 레이아웃은 모바일 스크린 너비로 인해 모바일 디바이스에서 일반적으로추천되지 않습니다.


  그러나, 버튼이나 네비게이션 탭과 같은 작은 요소로 위치시키고 싶을 때가 있습니다. 그러면 컬럼은 완벽하죠.


  그리드 안의 컬럼은 테두리, 배경, 마진이나 패딩이 없는 같은 너비입니다.


  5 가지의 레이아웃 그리드를 사용 할 수 있습니다:


Grid ClassColumnsColumn WidthsCorresponds ToExample
ui-grid-solo1100%ui-block-aTry it
ui-grid-a250% / 50%ui-block-a|bTry it
ui-grid-b333% / 33% / 33%ui-block-a|b|c Try it
ui-grid-c425% / 25% / 25% / 25%ui-block-a|b|c|dTry it
ui-grid-d520% / 20% / 20% / 20% / 20%ui-block-a|b|c|d|e

Try it


[출처: w3schools]




 Customize Grids

  CSS를 사용해서 커스터마이즈한 컬럼 블럭을 사용할 수 있습니다:


 * jqcustom.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>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<style>
.ui-block-a, .ui-block-b, .ui-block-c {
    background-color: lightgray;
    border: 1px solid black;
    height: 100px;
    font-weight: bold;
    text-align: center;
    padding: 30px;
}
</style>
</head>
<body>
 
<div data-role="page" id="pageone">
  <div data-role="header">
    <h1>Customized Columns</h1>
  </div>
 
  <div data-role="main" class="ui-content">
    <p>Three-column Styled Layout:</p>
    <div class="ui-grid-b">
      <div class="ui-block-a"><span>First Column</span></div>
      <div class="ui-block-b"><span>Second Column</span></div>
      <div class="ui-block-c"><span>Third Column</span></div>
    </div>
  </div>
</div
 
</body>
</html>
 
cs






 


 Multi Rows

  또한 컬럼 안에 여러 열을 가질 수 있습니다:


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>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
 
<div data-role="page" id="pageone">
  <div data-role="header">
    <h1>Multiple Rows</h1>
  </div>
 
  <div data-role="main" class="ui-content">
    <p>Three-column Layout:</p>
    <div class="ui-grid-b">
      <div class="ui-block-a" style="border:1px solid black;"><span>Some Text</span></div>
      <div class="ui-block-b" style="border:1px solid black;"><span>Some Text</span></div>
      <div class="ui-block-c" style="border:1px solid black;"><span>Some Text</span></div>
    </div>
 
    <p>Three-column Layout With Multiple Rows:</p>
    <div class="ui-grid-b">
      <div class="ui-block-a" style="border:1px solid black;"><span>Some Text</span></div>
      <div class="ui-block-b" style="border:1px solid black;"><span>Some Text</span></div>
      <div class="ui-block-c" style="border:1px solid black;"><span>Some Text</span></div>
      <div class="ui-block-a" style="border:1px solid black;"><span>Some Text</span></div>
      <div class="ui-block-b" style="border:1px solid black;"><span>Some Text</span></div>
      <div class="ui-block-a" style="border:1px solid black;"><span>Some Text</span></div>
    </div>
  </div>
</div
 
</body>
</html>
 
cs









 Responsive Grids

  작은 스크린에서, 많은 버튼이 나란히 놓여 있는 것은 추천되지 않습니다.


  반응형 그리드를 위해, ui-responsive 클래스를 컨테이너에 추가합니다:


 * jqresgrid.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
 
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
 
<div data-role="page" id="pageone">
  <div data-role="header">
    <h1>Responsive Layout</h1>
  </div>
 
  <div data-role="main" class="ui-content">
    <h3>Slowly resize the width of your browser window. The layout will adjust itself to fit the "new" width of the browser.</h3>
    <div class="ui-grid-b ui-responsive">
      <div class="ui-block-a">
        <a href="#" class="ui-btn ui-corner-all ui-shadow">First Column Button</a><br>
        <span>First Column: This is some text. This is some text. This is some text. This is some text. This is some text.</span>
      </div>
      <div class="ui-block-b">
        <a href="#" class="ui-btn ui-corner-all ui-shadow">Second Column Button</a><br>
        <span>Second Column: This is some text. This is some text. This is some text. This is some text.</span>
      </div>
      <div class="ui-block-c">
        <a href="#" class="ui-btn ui-corner-all ui-shadow">Third Column Button</a><br>
        <span>Third Column: This is some text. This is some text. This is some text. This is some text.</span>
      </div>
    </div>
  </div>
</div
 
</body>
</html>
 
cs










end






* 본 포스트는 w3school 을 통해 작성된 포스트입니다.

다른 카테고리의 글 목록

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

W3School 를 참고하여 작성하는 jQuery Mobile 강좌입니다.


아래 링크들은 Tutorial 편입니다.


jQ Mobile 시작하기

jQ Mobile 페이지(Pages)

jQ Mobile 버튼(Buttons)

jQ Mobile 아이콘(Icons)

jQ Mobile 팝업(Popups)

jQ Mobile 툴바(Toolbars)

jQ Mobile 네비바(Navibars)

jQ Mobile 패널(Panels)

jQ Mobile 접는 블록(Collapsibles)

jQ Mobile 테이블(Tables)

jQ Mobile 그리드(Grids)




jQuery Mobile Tables

 Responsive Tables

  반응형 디자인은 디바이스의 스크린 사이즈와 방향과 같은 사용자 디바이스의 모바일 웹 페이지에서의 내용에 대한 반응을 보일 때 굉장히 유용합니다.


  간단한 클래스 이름으로, jQuery Mobile은 특정 유저에 대한 적절한 내용을 보이기 위해 사용자의 스크린 사이즈와 자동적으로 리사이즈가 가능하도록 인식합니다.


  반응형 테이블은 큰 세트의 표로 나타낸 데이터를 모바일이나 데스크탑에서든 잘 보일 것입니다.


  두 가지 타입의 반응형 테이블이 있습니다: reflow & column toggle



 Reflow Table

  reflow 모드는 데이터가 가장 작은 사이즈에 도달할 때까지 테이블 데이터를 수평적으로 위치시킵니다. 그러고나서, 모든 열을 한꺼번에 수직적으로 그룹화 합니다.


  테이블을 만들고, data-role="table"을 추가하고 <table> 요소에 "ui-resopnsive" 클래스를 추가합니다:


 * jqtable.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
 
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
 
<div data-role="page" id="pageone">
  <div data-role="header">
    <h1>Reflow Table</h1>
  </div>
  
  <div data-role="main" class="ui-content">
    <p>Reflow Tables lay the table data horizontally until it reaches a minimum size, then all rows are grouped together in a vertical stack with the corresponding labels.</p>
    <p>In this example we have used the well-known Northwind sample database. Below is a selection from the "Customers" table.</p>
    <p>Try to resize the width of your browser window, to see the effect.</p>
    <table data-role="table" class="ui-responsive">
      <thead>
        <tr>
          <th>CustomerID</th>
          <th>CustomerName</th>
          <th>ContactName</th>
          <th>Address</th>
          <th>City</th>
          <th>PostalCode</th>
          <th>Country</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>1</td>
          <td>Alfreds Futterkiste</td>
          <td>Maria Anders</td>
          <td>Obere Str. 57</td>
          <td>Berlin</td>
          <td>12209</td>
          <td>Germany</td>
        </tr>
        <tr>
          <td>2</td>
          <td>Antonio Moreno Taquer�a</td>
          <td>Antonio Moreno</td>
          <td>Mataderos 2312</td>
          <td>M�xico D.F.</td>
          <td>05023</td>
          <td>Mexico</td>
        </tr>
        <tr>
          <td>3</td>
          <td>Around the Horn</td>
          <td>Thomas Hardy</td>
          <td>120 Hanover Sq.</td>
          <td>London</td>
          <td>WA1 1DP</td>
          <td>UK</td>
        </tr>
        <tr>
          <td>4</td>
          <td>Berglunds snabbk�p</td>
          <td>Christina Berglund</td>
          <td>Berguvsv�gen 8</td>
          <td>Lule�</td>
          <td>S-958 22</td>
          <td>Sweden</td>
        </tr>
      </tbody>
    </table>
  </div>
 
  <div data-role="footer">
    <h1>Footer Text</h1>
  </div>
</div
 
</body>
</html>
 
cs






  




 tip: 반응형 테이블이 제대로 작동하기 위해서, <thead>와 <tbody> 요소를 반드시 포함해야 합니다. rowspan이나 colspan 속성은 사용하지 마세요; 반응형 테이블에서 지원되지 않습니다.




 Column Toggle Table

  "column toggle" 모드는 데이터를 표시하기 충분한 공간이 없을 때 행을 숨깁니다.


  column toggle 테이블을 생성하기 위해서는, 아래처럼 <table> 요소에 추가해주시면 됩니다:


1
2
3
 
<table data-role="table" data-mode="columntoggle" class="ui-responsive" id="myTable">
 
cs



  기본적으로, jQuery Mobile은 오른 편의 테이블로부터 컬럼을 숨깁니다. 그러나, 특정 순서에 따라 어느 컬럼이 숨겨지고 보여지게 명시할 수 있습니다. 테이블의 헤더<th>에 data-priority 속성을 추가하고 1 부터 6까지의 숫자를 명시해주면 됩니다:


1
2
3
4
5
 
<th>I will never be hidden</th>
<th data-priority="1">I am very important - I will probably not be hidden</th>
<th data-priority="3">I am less important - I could be hidden</th>
<th data-priority="5">I am not that important - there is a good chance that I will be hidden</th>
cs




  위와 같이 작성을 하고 나면, 프레임워크는 자동적으로 테이블의 오른쪽 코너에 버튼을 추가합니다. 이 버튼은 사용자가 어떤 컬럼을 테이블에서 볼지 정하는 버튼입니다:



 * jqctable.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
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
 
<div data-role="page" id="pageone">
  <div data-role="header">
    <h1>Column Toggle Table</h1>
  </div>
  
  <div data-role="main" class="ui-content">
     <p>The "column toggle table" will hide columns when there is not enough width to display the data.</p>
    <p>In this example we have used the well-known Northwind sample database. Below is a selection from the "Customers" table.</p>
    <h4>Slowly resize the width of your browser window. The layout will adjust itself to fit the "new" width of the browser. OR, you can use the "Columns..." button to choose which column should be hidden or shown.</h4>
    <table data-role="table" data-mode="columntoggle" class="ui-responsive" id="myTable">
      <thead>
        <tr>
          <th data-priority="6">CustomerID</th>
          <th>CustomerName</th>
          <th data-priority="1">ContactName</th>
          <th data-priority="2">Address</th>
          <th data-priority="3">City</th>
          <th data-priority="4">PostalCode</th>
          <th data-priority="5">Country</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>1</td>
          <td>Alfreds Futterkiste</td>
          <td>Maria Anders</td>
          <td>Obere Str. 57</td>
          <td>Berlin</td>
          <td>12209</td>
          <td>Germany</td>
        </tr>
        <tr>
          <td>2</td>
          <td>Antonio Moreno Taquer�a</td>
          <td>Antonio Moreno</td>
          <td>Mataderos 2312</td>
          <td>M�xico D.F.</td>
          <td>05023</td>
          <td>Mexico</td>
        </tr>
        <tr>
          <td>3</td>
          <td>Around the Horn</td>
          <td>Thomas Hardy</td>
          <td>120 Hanover Sq.</td>
          <td>London</td>
          <td>WA1 1DP</td>
          <td>UK</td>
        </tr>
        <tr>
          <td>4</td>
          <td>Berglunds snabbk�p</td>
          <td>Christina Berglund</td>
          <td>Berguvsv�gen 8</td>
          <td>Lule�</td>
          <td>S-958 22</td>
          <td>Sweden</td>
        </tr>
 
      </tbody>
    </table>
  </div>
 
  <div data-role="footer">
    <h1>Footer Text</h1>
  </div>
</div
 
</body>
</html>
 
cs








 Styling Tables

  "ui-shadow" 클래스를 사용해서 테이블에 그림자를 추가할 수 있습니다:


1
2
3
 
<table data-role="table" data-mode="columntoggle" class="ui-responsive ui-shadow" id="myTable">
 
cs



  







end






* 본 포스트는 w3school 을 통해 작성된 포스트입니다.

다른 카테고리의 글 목록

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