본문 바로가기

AngularJS

18. 지시자 - 2 (사용자 정의 지시자)

1. 사용자 정의 지시자 개요

 - AngularJS는 지시자를 이용해 웹 UI 컴포넌트를 만들 수 있는 메커니즘을 제공한다.

우리가 만드는 UI 컴포넌트는 양방향 데이터 바인딩을 제공할 수도 있고 도메인에 특화된 HTML태그를 구성할 수도 있다.

가령 다음과 같은 태그를 만들 수도 있다.

<tabs>

<pane title="패널 1">

<title>패널 1</title>

<p>hello 패널 1</p>

</pane>


<pane title="패널 2">

<title>패널 2</title>

<p>hello 패널 2</p>

</pane>

</tabs>

2. 간단한 지시자 정의

 <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 메서드는 첫번째 인자로 지시자의 이름을 요구한다.

지시자 이름은 나타표기법으로 작성해야 한다.

그 다음으로 지시자 설정함수를 줄 수 있는데 이 함수에서 다른 서비스의 주입을 받고 싶을 때는 서비스 이름으로 인자를 정의하면 된다.

다음 코드는 지시자 함수 부분에 $log 서비스를 주입받는 코드이다.


angular.module('sampleApp', [])

.directive('hello', function($log){

return function(scope, iElement, iAttrs, controller, transcludeFn){

$log.log("<h1>hello "+iAttrs.name+"</h1>");

iElement.html("<h1>hello "+iAttrs.name+"</h1>");

};

});


 - directive 메서드에서 두 번째 인자인 지시자 설정함수는 함수나 객체를 반환해야 한다.

위 코드에서는 함수를 반환했는데 이 함수는 링크 함수이다.

링크함수는 해당 지시자가 적용된 DOM에 연결된 함수를 의미한다.

이 링크 함수에서는 순서대로 scope 객체, 연결된 요소 객체, 속성 객체, 컨트롤러 객체가 인자로 주어진다.


 - 각 인자별 설명

① scope 객체

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

② 연결된 요소 객체 

 - AngularJS는 제이쿼리와 뛰어난 호환성을 제공하지만 제이쿼리 없이도 사용할 수 있게 제이쿼리 라이트 버전을 제공하고 있다.

제이쿼리를 해당 페이지에서 사용하면 연결된 DOM을 감싸고 있는 제이쿼리 객체가 반환되고 그렇지 않으면 AngularJS가 구현한 제이쿼리 라이트 객체

가 봔환된다. 해당 객체에는 html(), append(), css() 등 제이쿼리에서 제공하는 DOM을 제어할 수 있는 다양한 메서드가 있다.

- 자세한 내용은 http://docs.angularjs.org/api/angular.element를 참고하면 된다.

③ 속성 객체

 - 연결된 요소에서 속성명과 속성값을 가지는 속성 객체다. 예를 들어, <tap title="hello"></tap> 라는 요소가 있을 때 해당 요소에 대한 속성 객체로서

title의 속성을 이용해 hello라는 값을 가지고 올 수 있다.

- 해당 객체에서 제공하는 메서드는 http://dosc.angularjs.org/api/ng.$compile.directive.Attributes 에서 참고하면 된다.

④ 컨트롤러 객체

 - 지시자에서는 해당 지시자가 사용하는 여러 요소가 공통으로 공유하는 컨트롤러를 정의할 수 있다.

여기서 컨트롤러는 ng-controller로 등록하는 컨트롤러가 아니라 지시자에서 등록하는 컨트롤러이다.

⑤ transclude 함수

 - transclude 옵션을 사용할 경우 삽입되는 페이지 영역에 전달할 scope에 대한 링크 함수를 정의한다.

삽입되는 페이지 영역에 적절한 scope를 미리 연결하기 위해 사용한다.


3. 지시자 설정 객체

 - 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에 여러 지시자가 적용될 수가 있다. 이때 우선순위를 줄 수 있는데 숫자로 주어야 하며 큰 숫자의 priority가 정의된 지시자가 

먼저 호출된다.

③ terminal (true/false)

 - 값이 true이면 지시자는 마지막에 호출된다. 기본갑은 false이다.

④ scope(true/false/객체)

 - 지시자에서 사용하는 scope 객체에 대한 설정 정보를 준다. 여기에 줄 수 있는 값은 true/false 또는 객체다 각값의 의미는 다음과 같다.

 ▶ true : 해당 지시자가 필요로하는 새로운 scope가 생성된다.

 ▶ false : 해당 지시자가 적용된 DOM은 별도의 scope를 생성하지 않는다. 해당 지시자를 감싸고 잇는 부모 DOM에서 사용하는 scope를 사용하게 된다.

대부분 ng-controller에서 생성한 scope를 사용한다.

 ▶ 객체 : 객체를 선언하면 새로운 scope를 생성한다. 하지만 부모 scope와의 상속은 없다. 완전 독립된 scope 객체가 생성된다.

이 객체에서는 지시자 내부에서 사용하지만 부모 scope와의 관계가 있는 속성들을 정의한다. 범하기 쉬운 실수로 해당 객체가 지시자와 연결된 모델이라고 생각하여 컨트롤러에서 하는 것과 비슷하게 모델을 정의하는데 이 객체는 부모 scope와의 관계를 정의하는 설정 정보를 넣는 곳이다.

다음은 객체 속성에 줄 수 있는 값이다. 예를 들어 {name: "@"} 또는 {name: "@title"}과 같은 형태로 줄 수 있다. 그러면 name은 해당 지시자에서 사용하는 scope에 대한 속성명이다.

 ▶ 다음은 속성 값으로 줄 수 있는 기호들 이다.

-"@" 또는 "@속성명"

연결된 DOM의 속성 값을 내부 scope의 속성과 연결한다. DOM 속성 값은 항상 문자열이므로 scope 속성의 값도 문자열이 된다.

"@"만 사용하게 되면 DOM 속성의 이름과 scope 내부 속성의 이름이 같다고 판단한다.

-"&" 또는 "&속성명"

부모 scope를 기준으로 주어진 표현식을 계산한다. 주어지 DOM의 속성값은 표현식이 되고 해당 표현식을 감싸는 함수의 레퍼런스가 

socpe 속성의 값으로 주어진다. 표현식을 감싸는 함수에 인자를 전달하고 싶을 경우 인자명을 속성명으로하고 속성값을 인자에 전달할

한 객체를 전달할 수 있다.

예를 들어, DOM의 속성값이 call(number)이면 지시자에서 call({number:"000-000"})으로 인자를 전달할 수 있다.

-"="또는 "=속성명"

부모 scope의 속성과 지시자 내부 scope의 속성 사이에 양방향 연결을 설정한다. 부모 scope의 속성은 DOM 속성의 값을 이용해 가져온다.

예를들어, 부모 scope가 {parentscopeName : "parent"}이고 지시자 내부 scope가 {dirscope : "=myAttr"}로 설정돼있다고 하면

<my-dir my-attr="parentscopeName"></my-dir>으로 DOM을 작성했다면 myDir 지시자 내부 scope의 dirscope에 "parent" 값이 바인딩 된다.

부모 scope의 parentscoeName의 값이 바뀔 때마다 myDir 내부 지시자 scope의 dirscope의 값도 같이 바뀌게 된다. 또한 반대로도 바뀌게 된다.

부모 scope에 연결된 속성이 없다면 NON_ASSIGNABLE_MODEL_EXPRESSION 에러가 발생하는데 에러가 발생하게 하지 않으려면 "=?"또는 "=?속성명"

으로 설정해야한다.


⑤ controller (함수)

 - 지시자에서 사용할 컨트롤러 함수. 컨트롤러 함수에는 다음과 같은 인자가 주입된다.

 $scope : 지시자에서 사용되는 scope

 $element : 지시자의 요소객체(제이쿼리나 제이쿼리 라이트로 DOM 객체를 감싸고 있다.

 $attr : 지시자의 속성 객체(AngularJS의 속성 타입)

 $transclude : transclude옵션을 사용할 경우 포함되는 페이지 영역에 전달할 scope에 대한 링크함수를 정의 한다.

삽입되는 페이지 영역에 적절한 scope를 미리 연결하기 위해 사용한다.


⑥ restrict("EACM" 문자조합) : 지시자가 호출되는 방식을 결정한다. "EACM" 문자 조합으로 문자열을 줄 수 있다. 각 문자의 의미는 다음과 같다.

  E : 요소명을 이용한 호출 예) <my-directive></my-directive>

  A : 요소의 속성을 이용한 호출 예) <span my-directive="expression"></span>

  C: 요소의 클래스를 이용한 호출 예) <span class="my-directive:expression;"></span>

  M : 코멘트를이용한 호출 예) <!-- directive : my-directive expression -->


⑦ template (문자열/함수) : 지시자의 템플릿을 설정한다. 지시자를 적용한 DOM은 해당 템플릿으로 대체된다. 

대신 DOM의 속성과 CSS 클래스는 모두 복사된다. 함수로 작성하게 되면 tElement와 tAttrs을 인자로 받을 수 있다.

tElement는 적용된 DOM의 제이쿼리 라이트나 제이쿼리 객체이고 tAttrs은 속성 객체다. 해당 함수는 템플릿을 문자열로 반환해야한다.


⑧ templateUrl(문자열/함수) : template과 똑같은 역할을 한다. 단, 템플릿을 주어진 url을 이요해 읽어들인다. template과 마찬가지로 함수를 설정값으로

줄 수 있다.해당 함수에서도 tElement와 tAttrs를 인자로 받을 수 있다. 해당 함수는 url을 문자열로 반환해야한다.


⑨ replace(true/false) : 적용된 HTML 요소를 교체할지 여부를 결정한다. true로 값을 주면 적용된 요소 자체가 템플릿으로 교체되고 false로 값을 주면

요소 자체는 바뀌지 않고 내부 내용만 바뀐다.


⑩ transclude(true/'element') : 적용된 HTML 요소의 내부 내용을 컴파일하여 지시자에서 사용할 수 있게 한다. 

컴파일한다는 의미

내부 내용에 표현식이 작성됐으면 해당 표현식의 계산이된다.

다른 지시자가 사용된 경우 다른 지시자와 연결된 지시자 함수의 결과가 내부 내용에 반영된다는 것이다. 

- 템플릿에서 ng-translcude 지시자를 이용하면 적용된 HTML 요소의 내부 내용에 접근할 수 있다.

true로 값을 줌년 적용한다는 의미고 "element라고 문자열을 주게되면 요소의 내부 내용뿐만아니라 요소 전체를 지시자에서 사용한다는 의미다.

요소에 적용된 다른 지시자까지 모두 컴파일된다.


⑪ compile(함수) : 컴파일 함수를 정의한다. 해당 함수에서는 적용된 DOM을 변경하는 데 사용한다. compile 함수는 반드시 link 함수나 pre와 post를 속성으로 

가지는 객체를 반환해야한다. pre속성에는 pre link 함수가 정의되고 post는 post link 함수가 정의되야 한다. 

그러므로 다음 link 설정과 같이 사용 할 수 없다. compile 함수에서 link 함수 또한 정의하기 때문이다. compile 함수에서 사용할 수 있는 인자는 다음과 같다

-tElement : 연결된 DOM을 감싸고 있는 jQueryLite 또는 제이쿼리 인스턴스다. ik 함수와 같다.

-tAttr : 속성 객체다. (AngularJS의 속성 타입)

-Link(함수) : 링크함수를 정의한다. DOM의 이벤트 리스너를 등록하거나 DOM을 변경하는 행위를 한다.

링크 함수는 템플릿이 컴파일된 뒤에 실행된다. 대부분 지시자의 로직을 여기서 구현하게 된다. 

해당 함수는 위의 compile 함수 설정과 같이 사용할 수 없다. 



4. 자체 템플릿을 가지는 지시자


<!DOCTYPE html>

<html ng-app="sampleApp">

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

<script type="text/javascript" src="js/lib/angular.js" ></script>

<script type="text/javascript">

angular.module('sampleApp',[])

.directive('hello', function(){

return{

//templateUrl: "template/helloTpl.html",

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="angularJS"></div>

</body>

</html>

위 예제는 두가지 아쉬움 점이 있다. 

하나는 템플릿을 변경하려면 지시자 함수가 정의된 자바스크립트 소스를 직접 수정해야한다는 것.

두 번째는 링크함수에서 템플릿의 마크업 구조를 정확하게 알고 있어야만 데이터를 제대로 표현할 수 있다는 것.

위 예제의 template 설정을 templateUrl로 변경할 수도 있다.

<!DOCTYPE html>

<html ng-app="sampleApp">

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

<script type="text/javascript" src="js/lib/angular.js" ></script>

<script type="text/javascript">

angular.module('sampleApp',[])

.directive('hello', function(){

return{

//template: "<h1>hello<span>name</span></h1>",

templateUrl: "template/helloTpl.html",

restrict : "AE",

link : function link(scope, iEl, iAt, ctrl){

//iEl.find("span").text(iAt.name);

scope.name=iAt.name;

}

}

});

</script>

</head>

<body>

<div hello name="angularJS"></div>

</body>

</html>

template/helloTpl.html 소스

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

위와 같이 예제 코드를 수정하면 템플릿 코드가 지시자 정의 코드와 명확히 분리되고 데이터를 표현하기 위해 문서구조를 정확히 알지 않아도 된다.

위와 같이 scope에 데이터를 연결하는 일은 사실 지시자 컨트롤러에서도 할 수 있다.

.directive('hello', function(){

return{

//template: "<h1>hello<span>name</span></h1>",

templateUrl: "template/helloTpl.html",

restrict : "AE",

/* link : function link(scope, iEl, iAt, ctrl){

//iEl.find("span").text(iAt.name);

scope.name=iAt.name;

} */

controller: function($scope, $element, $attrs, $transclude){

$scope.name=$attrs.name;

}

}

위 예제는 링크 함수를 정의하지 않고 지시자에 컨트폴러를 정의한 것이다.

하지만 위의 예제 코드들은 큰 문제점이 있다. 바로 scope 설정이 잘못된 것이다.

<div hello name="angularJS"></div>

<div hello name="google"></div>

<div hello name="naver"></div>

예제를 위와 같이 바꾸면 모두 hello naver가 나오게된다. 이유는 간단한다.

모두 같은 scope를 사용해서 마지막에 호출된 지시자가 이전의 scope.name의 값을 바꾸어 버렸기 때문이다.


5. scope 설정 완전 정복

지시자를 재사용할 수 있는 컴포넌트라 생각하고 만들려면 지시자가 사용하는 scope는 다른 지시자와 독립적인 공간이어야 하고 같은지시자가 

한 페이지에서 여러번 사용되어도 서로 영향을 주지 않아야 된다.

하지만 특정 DOM을 간단히 조작하는 경우라면 별도의 scope를 만들 필요가 없다.


1) 별도의 새로운 scopes를 만들지 않는 설정

 - 지시자 설정 객체에 scope 속성을 별도록 명시하지 않거나 scope:fasle로 하면 scope가 만들어지지 않는다.

위예제의 hello 지시자는 scope를 별도로 만들지 않으므로 hello 지시자 템플릿에 사용한 표현식은 부모 scope를 이용하게 된다.

그래서 지시자의 link 함수나 컨트롤러 함수에서 scope에 연결된 모델을 변경하면 다른 모든 지시자에 영향을 주게 된다.


2) 부모 scope를 상속받는 scope 설정

 - 지시자 설정 객체에 scope 속성에 true 값을 주면 부모 scope를 상속받는 새로운 scope가 생성된다.

 *scope 설정을 true로 한 예제 코드

<!DOCTYPE html>

<html ng-app="sampleApp">

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

<script type="text/javascript" src="js/lib/angular.js" ></script>

<script type="text/javascript">

angular.module('sampleApp', [])

.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="google"></div>

<div hello name="naver"></div>

<div hello></div>

</body>

</html>

위 예제는 hello 지시자 별로 부모 scope(demoCtrl의 scope)를 상속받는 cope가 생성된다.


3) @을 이용한 독립 scope 설정

독립 scope를 설정하려면 지시자 설정 객체의 scope 속성에 객체 리터럴을 이용해 객체를 선언해 주면된다.

그리고 해당 객체에서 부모 객체와의 연관성을 정의한다.

◈ scope 객체 속성명 설정 방법

 - @-> scope {name : "@" } //"@"만 사용하게 되면 DOM 속성의 이름과 scope 내부 속성의 이름이 같다고 판단한다.

 - @연결된 DOM 속성명-> scope : {name:"@to"} 


*독립된 scope를 가지는 hello 지시자 예제 코드

<!DOCTYPE html>

<html ng-app="sampleApp">

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

<script type="text/javascript" src="js/lib/angular.js" ></script>

<script type="text/javascript">

angular.module('sampleApp', [])

.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="google"></div>

<div hello to="naver"></div>

<div hello></div>

</body>

</html>

 - 위 예제는 독립된 scope를 사용하므로 부모 scope의 name 속성을 상속받지 않는다. 

또한 scope 설정에서 scope의 name 속성에 "@to"을 주어 지시자가 적용된 <div> 태그의 to 속성에 대한 값이 지시자 내부 scope의 name 속성에 연결됐다.


4) &을 이용한 독립 scope 설정

scope 설정에서 속성의 값으로 "&"나 "&연결된 DOM 속성명"을 주면 부모 scope의 환경에서 실행될 수 있는 표현식에 대한 레퍼런스(reference)를 가지고 

올 수 있다.


* helloTmpl2.html 코드

<h1>hello <span>{{name}}</span><button ng-click="send()">메시지 보내기</button></h1>

&을 이용한 독립 scope 설정 코드

<!DOCTYPE html>

<html ng-app="sampleApp">

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

<script type="text/javascript" src="js/lib/angular.js" ></script>

<script type="text/javascript">

angular.module('sampleApp', [])

.controller('demoCtrl', ['$scope', function($scope){

$scope.helloList=[{name:'google'}, {name:'naver'}, {name:'angular'}];

$scope.sendMessage = function(toSb){

console.log(toSb+"에게 메시지를 보낸다.");

};

}])

.directive('hello', function(){

return{

templateUrl:"template/helloTmpl2.html",

restrict : "AE",

scope : {

name:"@to",

send:"&"

}

}

});

</script>

</head>

<body ng-controller="demoCtrl">

<div ng-repeat="helloSb in helloList" hello to="{{helloSb.name}}" send="sendMessage(helloSb.name)">

</div>

</body>

</html>


5) =을 이용한 독립 scope 설정

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

scope : {name: "=to"}

위 코드에서 부모 scope의 to 속성과 양방향 데이터 바인딩이 됐다고 생각하기 쉬운데 지금까지 scope 설정과 같이 to는 부모 scope의 속성명이 아니라 연결된 DOM의 속성명이다.

이 연결된 DOM의 to라는 속성명에 대한 값이 바로 부모 scope의 속성명이 된다.


* helloTmpl3.html 소스 코드

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


* =을 이용한 독립 scope 코드

<!DOCTYPE html>

<html ng-app="sampleApp">

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

<script type="text/javascript" src="js/lib/angular.js" ></script>

<script type="text/javascript">

 angular.module('sampleApp', [])

 .controller('demoCtrl',['$scope', function($scope){

$scope.helloList = [{name: 'google'}, {name: 'naver'}, {name: 'angular'}];

 }])

 .directive('hello', function(){

return{

templateUrl : "template/helloTmpl3.html",

restrict : "EA",

scope: {

name : "=to"

}

}

 });

</script>

</head>

<body ng-controller="demoCtrl">

<ul>

<li ng-repeat="helloSb in helloList">

<input type="text" ng-model="helloSb.name">

</li>

</ul>

<div ng-repeat="helloSb in helloList" hello to="helloSb.name"></div>

</body>

</html>

6. ngTransclude와 transclude 설정

 * panelTmpl.html 코드

<div class="panel {{type}}">

<div class="panel-title">{{title}}</div>

<div class="panel-content"><div ng-transclude></div></div>

</div>

 * ngTransclude 적용 코드

<!DOCTYPE html>

<html ng-app="sampleApp">

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

<script type="text/javascript" src="js/lib/angular.js" ></script>

<style type="text/css">

.panel{

margin: 10px;

-moz-border-radius:2px;

-webkit-border-radius: 2px;

border-radius: 2px;

border: 1px solid black;

}

.panel.info .panel-title{

background-color: gray;

color: white;

}

.panel.alert .panel-title{

background-color: red;

color: white;

}

.panel .panel-title{

background-color: black;

color: white;

padding: 10px;

}

.panel .panel-content{

padding: 10px;

}

</style>


<script type="text/javascript">

angular.module('sampleApp', [])

.controller('demoCtrl', ['$scope', function($scope){

$scope.noticeList = [{

url:"notice/1",

text:"공지사항 첫 번째 글입니다."

},

{

url:"notice/2",

text:"공지사항 두 번째 글입니다."

},

{

url:"notice/3",

text:"공지사항 세 번째 글입니다."

}];

}])

.directive('panel', function(){

return {

templateUrl : "template/panelTmpl.html",

restrict : "AE",

transclude: true,

scope : {

title:"@",

type:"@"

}

}

});

</script>

</head>

<body ng-controller="demoCtrl">

<panel title="알림" type="alert">

<p>AngularJS는 자바스크립트 웹 어플리케이션을 쉽게 개발하게 도와줍니다.</p>

</panel>

<panel title="공지사항 목록" type="info">

<ul>

<li ng-repeat="notice in noticeList">

<a href="{{notice.url}}">{{notice.text}}</a>

</li>

</ul>

</panel>

</body>

</html>

 - 지시자 설정 부분에 transclude : true 값을 주면 panelTmpl.html에서 ng-transclude라고 설정된 부분에 <panel>요기 내용이</panel> 태그의 내용이 복사된다.

 - AngularJS의 transclude를 사용하면 커테이너와 같은 역할을 하는 UI 컴포넌트를 재사용하도록 만들수 있다.