Highchart에 대한 Directive를 만들며서 controller, compile, link 등을 써보고 있는데, 성능 최적화 및 실시간 데이터 업데이트 방법등에 대해서 고민하면서 좋은 글이 있어서 머리도 정리할 겸 따라쟁이 해본다.
1. Directive 사용 영역
- HTML안에서 element, attribute, class name, comment 4영역
<angular></angular>
<div angular></div>
<div class="angular"></div>
<!-- directive: angular -->
2. 이름 규칙
- HTML안에서 하이픈(hyphens) 으로 단어 사이를 구분하여 사용
- JavaScript안에서 단어사이는 대문자로 구분하여 사용
<div the-quick-silver-rider-youngsik></div>
App.directive('theQuickSilverRiderYoungsik', function() { ... });
3. 테스트전 SaaS
- http://plnkr.co/ 코드 테스트 Preview 서비스를 이용한다
- GitHub 아이디로 로그인 한후 -> [Launch the Editor] 한다
- 자신의 테스트 코드 및 타인의 코드를 서로 공유할 수 있다
- 해당 서비스 자체가 AngularJS 기반으로 만들어 졌다 (Node.js + AngularJS 소스 공개)
- 그림 오른쪽의 angular.js 라이브러리 버전을 선택하면 index.html에 자동 삽입되고 코딩을 시작할 수 있다
4. 간단한 테스트 수행
- 사용자 정의 태그를 만든다
- 이름 : angular
// index.html
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 3) add the created custom 'angular' directive -->
<angular></angular>
</body>
</html>
// script.js
var
App = angular.module(
'App'
, []);
App.directive(
'angular'
,
function
() {
return
{
restrict:
'ECMA'
,
link:
function
(scope, element, attrs) {
var
img = document.createElement(
'img'
);
if
(element[0].nodeType === 8) {
element.replaceWith(img);
}
else
{
element[0].appendChild(img);
}
}
};
});
- plunker preview
- <angular> 태그에 <img> 태그가 추가되었다
5. Directive Definition
- AngularJS는 기동될 때(bootstrapped), 사용자정의 및 빌트인된 directives를 찾고 $compile() 메소드를 이용하여 컴파일을 한다
- compile 메소드 역할 : 모든 directives 요소를 찾아서 우선순위 sort하고 link function을 만든다. 또한 scope와 element사이의 2-way data binding을 위하여 $watch를 셋팅하거나 event listener를 생성된 (ng-app, ng-controller, ng-include, etc)같은 scope에 link한다
- Directive 정의
var
myModule = angular.module(...);
myModule.directive(
'directiveName'
,
function
(injectables) {
return
{
restrict:
'A'
,
template:
'<div></div>'
,
templateUrl:
'directive.html'
,
replace:
false
,
priority: 0,
transclude:
false
,
scope:
false
,
terminal:
false
,
require:
false
,
controller:
function
($scope, $element, $attrs, $transclude, otherInjectables) { ... },
compile:
function
compile(tElement, tAttrs, transclude) {
return
{
pre:
function
preLink(scope, iElement, iAttrs, controller) { ... },
post:
function
postLink(scope, iElement, iAttrs, controller) { ... }
}
},
link:
function
postLink(scope, iElement, iAttrs) { ... }
};
});
5-1. Compile function
- compile function이 link function을 만든다(produce)
- 2개 파라미터
+ tElement : directive가 선언되어진 template element (jQuery object이다. 따라서 $로 다시 wrap하지 마라)
+ tAttrs : tElement와 관련된 모든 attributes 목록
- ng-repeat같은 것을 사용할 경우 성능상의 이슈로 compile function이 사용된다. DOM과 scope객체를 연결지으려는데 데이터를 가져올 때마다 바인드과정을 거치지 않고 최초 한번 바인드되고 link functioin을 통해서 데이터 변경시에 scope-DOM간의 2-way 바인딩을 하여 변경을 반영한다
// in index.html
<div compile-check></div>
// in script.js
App.directive('compileCheck', function() {
return {
restrict: 'A',
compile: function(tElement, tAttrs) {
tElement.append('Added during compilation phase!');
}
};
});
- index.html에 <div compile-check></div>를 추가하고 script.js에 코드를 추가하면 하단에 'Added during compilation phase!'가 첨부된다
5-2. Link function
- DOM과 scope 객체를 2-way data binding 하는 역할이다. compile function에서는 scope 객체에 접근할 수 없다
- $watch 메소드를 통하여 사용자정의 listener를 생성할 수 있다. (register등록은 angular가 자동으로 해준다)
- 3개 파라미터
+ element : directive와 바인드된 요소
+ scope : directive에 의해 사용되어지는 scope 객체
+ attrs : 요소와 관련된 모든 attribute목록
5-3. Restrict
- E : elements
- A : attributes
- C : class name (CSS)
- M : comments
- HTML5 호환 표기가 가능한다 : 빌트인 ng-bind 사용
<
span
ng:bind
=
"name"
></
span
>
<
span
ng_bind
=
"name"
></
span
>
<
span
ng-bind
=
"name"
></
span
>
<
span
data-ng-bind
=
"name"
></
span
>
<
span
x-ng-bind
=
"name"
></
span
>
5-4. Template
- html에 넣은 요소와 교체할 내역
- html안의 태그 반복되는 부분을 별도의 모듈화로 중복을 방지 할 수 있다
- https://api.github.com/repos/angular/angular.js 호출하면 angular.js 관련 정보가 JSON 형태로 나옴
// index.html
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 3) add controller & whoiam directive -->
<div ng-controller="DemoCtrl">
<div whoiam>This text will be overwritten</div>
</div>
</body>
</html>
// script.js
var App = angular.module('App', []);
App.run(function($rootScope) {
$rootScope.header = 'I am bound to rootScope';
});
App.controller('DemoCtrl', function($scope) {
$scope.footer = 'I am bound to DemoCtrl';
});
App.directive('whoiam', function($http) {
return {
restrict: 'A',
template: '{{header}}<div class="thumbnail" style="width: 80px;"><div><img ng-src="{{data.owner.avatar_url}}"/></div><div class="caption"><p>{{data.name}}</p></div></div>{{footer}}',
link: function(scope, element, attrs) {
$http.get('https://api.github.com/repos/angular/angular.js').success(function(data) {
scope.data = data;
});
}
};
});
- $scope는 $rootScope를 상속받기 때문에 template에서 {{header}}, {{footer}}를 호출 할 수 있다
5-5. TemplateUrl
- 템플릿 마크업 및 별도의 html 파일로 관리한다
- template 파일의 로딩을 빠르게 할려면 메모리에 적제하여 사용한다. $templateCache
- <script> 태그를 이용한다
<script type=
'text/ng-template'
id=
'whoiam.html'
></script>
- inde.html 내역 : template script 태그를 head 태그안에 넣는다. id로 whoiam.html을 사용한다
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
<!-- 3) add the template script tag -->
<script type='text/ng-template' id='whoiam.html'>
<div class="thumbnail" style="width: 260px;">
<div><img ng-src="{{data.owner.avatar_url}}" style="width:100%;"/></div>
<div class="caption">
<p><b>Name: </b>{{data.name}}</p>
<p><b>Homepage: </b>{{data.homepage}}</p>
<p><b>Watchers: </b>{{data.watchers}}</p>
<p><b>Forks: </b>{{data.network_count}}</p>
</div>
</div>
</script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 4) add controller & whoiam directive -->
<div ng-controller="DemoCtrl">
<div whoiam>This text will be overwritten</div>
</div>
</body>
</html>
- script.js 내역
var App = angular.module('App', []);
App.controller('DemoCtrl', function($scope) { });
App.directive('whoiam', function($http) {
return {
restrict: 'A',
templateUrl: 'whoiam.html',
link: function(scope, element, attrs) {
$http.get('https://api.github.com/repos/angular/angular.js').success(function(data) {
scope.data = data;
});
}
};
});
- 호출한 angular.js 정보가 출력된다
5-6. Replace
- directive를 설정한 태그를 템플릿 태그로 교체하고자 할때 따라서 template 또는 templateUrl과 함께 사용한다
- replace: true
- index.html : elment, attribute, class name, comment 4가지 설정
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 3) add angular directive -->
<div angular></div>
<div class="angular"></div>
<angular></angular>
<!-- directive: angular -->
</body>
</html>
- script.js 내역
var App = angular.module('App', []);
App.directive('angular', function() {
return {
restrict: 'ECMA',
template: '<img src="http://goo.gl/ceZGf" />',
replace: true
};
});
- 결과 html
// attribute
<img src="http://goo.gl/ceZGf" angular=""></img>
// class name
<img class="angular" src="http://goo.gl/ceZGf"></img>
// element
<img src="http://goo.gl/ceZGf"></img>
// comment
<img src="http://goo.gl/ceZGf" angular=""></img>
- img 태그가 4개 생성되어서 이미지가 4개 보인다
5-7. Priority
- 우선순위에 따라서 compile/link의 순위를 정한다. 값이 클 수록 우선순위가 높다
- 이것은 미리 컴파일된 directive의 결과값을 체크하여 수행할 필요가 있을 때 사용한다
- zero이면 priority 설정을 하지 않고 default priority를 사용한다
- bootstrap의 'btn-primary' 클래스를 적용하려고 할때 index.html
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link href="style.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<script src="script.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 3) add angular directive -->
<div style='padding:100px;'>
<div primary btn>The Hitchhiker Guide to the Directive Demo by Dowon</div>
</div>
</body>
</html>
- script.js 내역 : 우선순위로 btn이 먼저 생성되었으므로 primary에 대한 적용이 가능해 진다
var App = angular.module('App', []);
App.directive('btn', function($timeout) {
return {
restrict: 'A',
priority: 1,
link: function(scope, element, attrs) {
element.addClass('btn');
}
};
});
App.directive('primary', function($http) {
return {
restrict: 'A',
priority: 0,
link: function(scope, element, attrs) {
if (element.hasClass('btn')) {
element.addClass('btn-primary');
}
}
};
});
- bootstrap의 btn-primary CSS가 적용된 버튼이 보인다
5-8. Terminal
- 가장 최근에 수행된 directive에 terminal: true를 설정하면 그 이후 directive는 수행되지 않는다
- index.html 내역
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link href="style.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<script src="script.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 3) add angular directive -->
<div first second></div>
<ul>
<li ng-repeat="item in ['one', 'two', 'three']" no-entry>{{item}} </li>
</ul>
</body>
</html>
- script.js 내역 : first -> second -> noEntry 가 수행되지만 second에 terminal: true 설정으로 매noEntry는 수행되지 않는다
- ng-repate, ng-switch는 priority 높음에도 나중에 수행됨. 버그?
var App = angular.module('App', []);
App.directive('first', function() {
return {
restrict: 'A',
priority: 3,
link: function(scope, element, attrs) {
element.addClass('btn btn-success').append('First: Executed, ');
}
};
});
App.directive('second', function() {
return {
restrict: 'A',
priority: 2,
terminal: true,
link: function(scope, element, attrs) {
element.addClass('btn btn-success').append('Second: Executed ');
}
};
});
App.directive('noEntry', function() {
return {
restrict: 'A',
priority: 1000,
link: function(scope, element, attrs) {
element.append('No Entry: Executed ');
}
};
});
- first, second의 priority 값을 서로 바꾸어 보라 : 버튼의 text 순서가 바뀔 것이다. 또는 noEntry의 priority값을 1000이하로 하면 directive가 실행된다
5-9. Controller
- Directive를 제어를 담당한다.
- $scope, this 를 사용하여 properties/methods를 바인할 수 있다
- this 키워드로 바인딩된 데이터는 require옵션을 사용하여 controller를 injecting함으로써 다른 directives에서 엑세스할 수 있게 한다.
- 5-10의 require에서 예제 진행함
App.directive(
'powerSwitch'
,
function
() {
return
{
restrict:
'A'
,
controller:
function
($scope, $element, $attrs) {
$scope.state =
'on'
;
$scope.toggle =
function
() {
$scope.$apply(
function
() {
$scope.state = ($scope.state ===
'on'
?
'off'
:
'on'
);
});
};
this
.getState =
function
() {
return
$scope.state;
};
},
};
});
5-10. Require
- controller를 다른 directive에 전달해서 compile/linking function안에 연관시킬 수 있다
- 같은 element 또는 부모와 바인드 되어야 한다
- prefixed 부호가 붙는다
+ ? : 지정한 directive가 없더라도 에러가 발생시키지 않는다
+ ^ : 부모 element위의 directive를 찾는다. 같은 element위치는 유효하지 않다
- index.html 내역
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link href="style.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<script src="script.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 3) add angular directive : parent -> child depth -->
<div class="btn btn-success" power-switch>
{{'Switched ' + state | uppercase}}
<div lightbulb class='bulb' ng-class="bulb"></div>
</div>
</body>
</html>
- script.js 내역
var App = angular.module('App', []);
App.directive('powerSwitch', function() {
return {
restrict: 'A',
controller: function($scope, $element, $attrs) {
$scope.state = 'on';
$scope.toggle = function() {
$scope.$apply(function() {
$scope.state = ($scope.state === 'on' ? 'off' : 'on');
});
};
this.getState = function() {
return $scope.state;
};
},
link: function(scope, element, attrs) {
element.bind('click', function() {
scope.toggle();
});
scope.$watch('state', function(newVal, oldVal) {
if (newVal === 'off') {
element.addClass('disabled');
} else {
element.removeClass('disabled');
}
});
}
};
});
App.directive('lightbulb', function() {
return {
restrict: 'A',
require: '^powerSwitch',
link: function(scope, element, attrs, controller) {
scope.$watch(function() {
scope.bulb = controller.getState();
});
}
};
});
- 변경내역 : toggle off일 경우
<div class="btn btn-success ng-binding disabled" power-switch="">
SWITCHED OFF
<div class="bulb off" ng-class="bulb" lightbulb=""></div></div>
- 결과 화면 : uppercase 필터 사용
5-11. Scope
- scope가 hierarchy하게 생성/유지되면 element와 부모 element사이의 scope를 설정한다.
- 그러나 자신은 부모 Scope하고만 바인드되어 data에 접근 할 수 있다. 최상위 scope는 $rootScope 객체이다
- scope: false
+ 새로운 scope 객체를 만들지 않고 부모가 가진 같은 scope 객체를 공유한다
- scope: true
+ 새로운 scope 객체를 만들고 부모 scope 객체를 상속받는다
- index.html 내역
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 3) add angular directive : parent -> child depth -->
<div class='well'>
Bound to $rootScope: <input type="text" ng-model="parent">
<div child></div>
</div>
<div class='form well' ng-controller="OneCtrl">
ng-controller="OneCtrl"<br/>
<input type="text" ng-model="children"><br/>
I am {{children}} and my grandfather is {{parent}}
<div child></div>
</div>
</body>
</html>
- script.js 내역 : child 디렉티브의 link에서 conole.log(scope)를 찍어본다
var App = angular.module('App', []);
App.run(function($rootScope) {
$rootScope.parent = 'ROOT';
});
App.directive('child', function() {
return {
restrict: 'A',
// scope: false,
scope: true,
template: 'I am a directive and my father is {{parent}} as well as {{children || "NIL"}}',
link: function(scope, element, attrs) {
console.log(scope);
}
};
});
App.controller('OneCtrl', function($scope) {
$scope.children = 'The One';
});
- 결과화면 : child의 scope: true, false로 변경해 가면서 console.log의 내역을 비교해 본다
- scope: true
// 첫번째 child의 $parent객체를 보면 ROOT이다
// 두번째 child의 $parent 객체를 보면 controller의 scope.children='The One' 값이 보임. 즉, controller scope가 부모이다
// Chrom에서 보면 scope 구조도가 보인다
- scope: false
// 첫번째 child의 $parent객체를 보면 ROOT이다
// 두번째 child의 $parent 객체를 보면 ROOT 이다
// 002 는 ROOT scope 이고 그 밑으로 controller scope만 존재하고 child는 이것을 공유한다
- scope: 'isolate'
+ 부모 scope를 상속받지 않는다. 그러나 scope.$parent로 부모 scope를 억세스 할 수 있다
+ iscope.$parent을 사용하지 않고 isolated scope와 부모 scope간의 데이터 공유방법은 어떻게 하는가?
isolated scope는 당신이 부모 scope로 부터 properties를 끌어내어 local scope와 바인드하는 object/hash를 갖는다
1) @ : 부모 scope property 와 local scope 값을 바인딩한다. 원하는 값 전달시 {{}} 를 사용한다
2) = : 부모 scope (전달전 측정되어질) property 를 직접 바인딩 한다
3) & : 속해있는 scope context안에서 수행될 expression 또는 method 와 바인딩 한다
- prefix parent property @, children property =, shout() method & 를 준 예제
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link rel="stylesheet" type="text/css"
href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body>
<h1>Hello Angular</h1>
<!-- 3) add angular directive : parent -> child depth -->
<div class='well'>
Bound to $rootScope: <input type="text" ng-model="parent">
<div child parent='{{parent}}' shout="shout()"></div>
</div>
<div class='form well' ng-controller="OneCtrl">
ng-controller="OneCtrl"<br/>
<input type="text" ng-model="children"><br/>
I am {{children}} and my grandfather is {{parent}}
<div child parent="{{parent}}" children="children" shout="shout()"></div>
</div>
</body>
</html>
- script.js 내역
r App = angular.module('App', []);
App.run(function($rootScope) {
$rootScope.parent = 'ROOT';
$rootScope.shout = function() {
alert("I am bound to $rootScope");
};
});
App.directive('child', function() {
return {
restrict: 'A',
scope: {
parent: '@',
children: '=',
shout: '&'
},
template: '<div class="btn btn-link" ng-click="shout()">I am a directive and my father is {{parent || "NIL"}} as well as {{children || "NIL"}}</div>'
};
});
App.controller('OneCtrl', function($scope) {
$scope.children = 'The One';
$scope.shout = function() {
alert('I am inside ng-controller');
};
});
- 결과화면
* scope: isolate 에 대한 이해는 egghead.io 사이트를 참조한다
5-12. Transcude
- ngTransclude를 통하여 DOM에 transcluded DOM을 insert 할 수 있다
- transclude: true
transcluded DOM을 template에서 ngTransclude directive에 삽입한다
// index.html
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
<!-- 3) template -->
<script type='text/ng-template' id='whoiam.html'>
<div class="thumbnail" style="width: 260px;">
<div><img ng-src="{{data.owner.avatar_url}}" style="width:100%;"/></div>
<div class="caption">
<p><b>Name: </b>{{data.name}}</p>
<p><b>Homepage: </b>{{data.homepage}}</p>
<p><b>Watchers: </b>{{data.watchers}}</p>
<p><b>Forks: </b>{{data.network_count}}</p>
<marquee ng-transclude></marquee>
</div>
</div>
</script>
</head>
<body style='padding:30px;'>
<div whoiam>I got transcluded</div>
</body>
</html>
// script.js
var App = angular.module('App', []);
App.directive('whoiam', function($http) {
return {
restrict: 'A',
transclude: true,
templateUrl: 'whoiam.html',
link: function(scope, element, attrs) {
$http.get('https://api.github.com/repos/angular/angular.js').success(function(data) {
scope.data = data;
});
}
};
});
// DOM 내역
<div whoiam="">
<div class="thumbnail" style="width: 260px;"><div>
<img style="width:100%;" ng-src="https://secure.gravatar.com/avatar/f0d91e5cf8ad1ce7972cc486baa20c42?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-org-420.png" src="https://secure.gravatar.com/avatar/f0d91e5cf8ad1ce7972cc486baa20c42?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-org-420.png"></img>
</div>
<div class="caption">
<p class="ng-binding"> … </p>
<p class="ng-binding"> … </p>
<p class="ng-binding"> … </p>
<p class="ng-binding"> … </p>
<marquee ng-transclude="">
<span class="ng-scope">
I got transcluded
</span>
</marquee>
</div>
// 결과 화면 : I got transcluded 메세지가 오른쪽에서 왼쪽으로 움직인다
- transclude: 'element'
+ transclude link function을 compile function안에 정의한다
// index.html
<!DOCTYPE html>
<!-- 1) add the ng-app attribute -->
<html ng-app="App">
<head>
<!-- 2) add the angular library -->
<script data-require="angular.js@1.1.5"
data-semver="1.1.5"
src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body style='padding:30px;'>
<div transclude-element>I got transcluded <child></child></div>
</body>
</html>
// script.js
var App = angular.module('App', []);
App.directive('transcludeElement', function() {
return {
restrict: 'A',
transclude: 'element',
compile: function(tElement, tAttrs, transcludeFn) {
// return 펑션이 link 펑션이 된다. 해당 펑션안에서 scope 객체 데이터 바인딩을 제어한다
return function (scope, el, tAttrs) {
transcludeFn(scope, function cloneConnectFn(cElement) {
tElement.after('<h2>I was added during compilation </h2>').after(cElement);
});
};
}
};
});
App.directive('child', function(){
return {
restrict: 'E',
link: function($scope, element, attrs) {
element.html(' with my child');
}
};
});
// DOM 내역
<body style="padding:30px;">
<!-- transcludeElement: -->
<div class="ng-scope" transclude-element="">
I got transcluded
<child>
with my child
</child>
</div>
<h2>
I was added during compilation
</h2>
</body>
// 결과 화면
<참조>
- 원문 : The Hitchhiker’s Guide to the Directive
- Scoep Isolate 참조 : egghead.io 사이트
- Yeoman 사용하기