본문 바로가기

Dev/Dev:: JavaScript

[AngularJS 1.x] AngularJS - 05. 지시자(Directive)



지시자(Directive)

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


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


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


- 지시자 함수 : AngularJS에서는 해당 DOM과 연결된 하나의 함수를 만들고 이 함수가 DOM을 조작하여 새로운 기능을 추가하는 등의 행위를 한다.

지시자 함수는 연결된 특정 DOM에 $scope를 연결하거나 연결된 DOM을 조작(manipulate)하여 특정 행위를 정의 할 수 있다.



지시자(Directive) 사용

- 지시자의 이름은 낙타표기법(camel case)으로 작성한다.

이런 지시자의 이름을 HTML(템플릿)에서는 영문자 중간에 대문자로 시작되는 부분에 : , -, 또는 _문자를 넣고 대문자를 소문자로 바꾸어 사용할 수 잇다.

예) myDirective라는 이름의 지시자가 있다면 이를 my-directive, my:directive, my_directive와 같은 형태로 HTML에서 사용한다.


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

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

  예) 

  <span my-directive="expression"><span>

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

  예) 

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

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

  예) 

  <my-directive></my-directive>

 4. 코멘트를 이용한 호출

  예) 

  <!-- directive: my-directive expression-->


* 사용 예제

hello라는 Text에 빨간색 스타일을 적용하는 코드이다.

여기서 첫번째는 요소의 속성을, 두번째는 요소의 클래스를 이용한 호출을 사용하였다.

  
<!DOCTYPE html>
<html ng-app="sampleApp">
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js" ></script>
<script type="text/javascript">
  angular.module('sampleApp', [])
    .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();">hello</p>
  <ng-form name="groupForm">
    <form name="formA"><input type="text"></form>
    <form name="formB"><input type="text"></form>
  </ng-form>
</body>
</html>




내장 지시자(Directive)

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


https://docs.angularjs.org/api


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




사용자 정의 지시자(Directive)

AngularJS의 가장 큰 매력중의 하나는 지시자를 이용해 웹 UI 컴포넌트를 만들 수 있다는 것이다.

직접 만든 컴포넌트는 양방향 데이터 바인딩을 제공할 수도 있고 도메인 특화된 HTML 태그를 구성할 수도 있다.


* 간단한 지시자 정의

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

<!DOCTYPE html> <html ng-app="sampleApp"> <head> <meta charset="UTF-8"> <script type="text/javascript" src="js/lib/angular.js" ></script> <script type="text/javascript"> angular.module('sampleApp', []) .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>

위 예제에서 directive 메서드를 사용한 코드를 살펴보자. 

- directive 메서드는 첫 번째 인자로 지시자의 이름을 요구한다. 다음으로 지시자 설정 함수를 줄 수 있는데, 함수에서 다른 서비스의 주입을 받고 싶을 때는 서비스 이름으로 인자를 정의하면 된다.

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

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





* 지시자 설정 객체
- directive 메서드의 첫 번째 인자로는 지시자 이름을 주고 그 다음으로는 지시자 설정함수를 준다.
이 설정 함수가 함수를 반환하면 링크 함수를 반환하는 것이고 객체를 반환하면 설정객체를 반환하는 것이다.
이렇게 지시자 설정 함수에서 반환되는 객체를 지시자 설정 객체라 한다.
이 설정 객체로 AngularJS가 지시자를 만들게 된다.

위의 hello 예제를 지시자 설정 객체로 작성한 코드이다.

<!DOCTYPE html> <html ng-app="sampleApp"> <head> <meta charset="UTF-8"> <script type="text/javascript" src="js/lib/angular.js" ></script> <script> angular.module('sampleApp', []) .directive('hello', function($log){ return { name : 0, 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 preList(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>"); } } //또는 //return function postLink(scope, iElement, iAttrs, controller, transcludeFn){} } //또는 //link: { //pre: function preLink(scope, iElement, iAttrs, controller, transcludeFn){}, //post: function postLink(scope, iElement, iAttrs, controller, transcludeFn){} //} //또는 //link : function postLink(scope, iElement, iAttrs, controller){} }; }); </script> </head> <body> <div hello name="angularJS"></div> </body> </html>


* 지시자 설정 객체의 설정 정보

  - 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 속성을으로 가지는 객체를 반환해야 한다.



* 자체 템플릿을 가지는 지시자

이번에는 링크 함수에서 DOM을 직접 생성하지 않고 template 설정을 이용해 생성하려는 DOM을 템플릿화 한다.

설정정보의 template와 templateUrl 정보를 이용하여 자체 템플릿을 가지는 지시자를 사용할 수 있다.

<!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>