* 실행 결과1: http://study.jeju.onl/cust-directives.html
* 실행 결과2: http://study.jeju.onl/jqlite-directives.html
커스텀 디렉티브는
내장 디렉티브에서 필요한 기능을 제공하지 못할 때,
복잡한 기능을 HTML이 아니라 코드로 표현하려고 할 때,
여러 애플리케이션에서 활용할 만한 기능 모듈을 구현하려고 할 때
활용한다.
- 커스텀 디렉티브 구현
– 디렉티브 정의: module.directive()
– 링크함수 구현: function( scope, element, attrs)
– 스코프로부터 데이터 가져오기: scope[ attrs[“속성명”] ]
– HTML 엘리먼트 생성: angular.element(), element.append()
– 데이터 속성 의존성 제거: 명시적인 속성명 가져오기
– 표현식 평가: 필터 사용을 위해
– 데이터 변화 처리: $watch() 함수를 이용해야 한다
– 스코프 문제 처리: 즉시 호출 함수 표현식(IIFE) - jqLite 활용
– 문서 객체 모델 탐색: children(), eq(index), find(tag), next(), parent()
– 엘리먼트 수정: addClass(), attr(), css(), hasClass(), prop(), removeAttr(), removeClass(), text(), toggleClass(), val()
– 엘리먼트 생성 및 제거: angular.element(), after(), append(), clone(), prepend(), remove(), replaceWith(), wrap()
– 이벤트 처리: on(), off(), triggerHandler()
– 기타 jqLite 메서드: data(key), removeData(key), html(), ready(handler) - jqLite를 통한 AngularJS 기능 접근 (실제로는 거의 사용 안함)
– controller(name), injector(), isolatedScope(),
scope(), inheritedData(key) - 제이쿼리를 통한 jqLite 대체
– appendTo() 등등
이 장에서 나온 중요한 내용
즉시 실행 패턴 함수 (IIFE)
함수를 선언이 아닌 표현식(Expression)으로 기술하는 것인데, 이것은 인터프리터가 해당 라인에 도달 하였을때만 실행이 됩니다. 즉, 함수의 실행시점을 제어합니다.
예제 코드입니다.
foo(); // success! function foo() { alert('foo'); } foo(); // "foo" is not defined. var foo = function() { alert('foo'); }; alert(foo); // "foo" is not defined. (function foo () {}); alert(foo); // "foo" is not defined.
이에 관련된 내용은 다음 문서들을 참고함!
참고1 http://chanlee.github.io/2014/01/11/understand-javascript-iife/
참고2 http://ohgyun.com/474
참고3 http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife
실행 결과에 대한 소스코드는 다음과 같다.
1) cust-directives.html
<!DOCTYPE html> <html ng-app="exampleApp"> <head> <title>Custom Directives - ProAngularJS</title> <link href="/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" /> <link href="/node_modules/bootstrap/dist/css/bootstrap-theme.css" rel="stylesheet" /> <!-- 주의!!: jQuery 스크립트 파일이 angular 보다 먼저 로드되어야 한다 ==> appendTo()를 인식하지 못함 --> https://code.jquery.com/jquery-1.11.3.min.js /node_modules/angular/angular.js /node_modules/angular/angular-route.js angular.module("exampleApp", []) .directive("unorderedList", function() { // 주의: 일반 자바스크립트 인자라서 전달객체의 순서가 정해져 있다 return function(scope, element, attrs) { // scope로부터 데이터를 전달받는 방법 var data = scope[ attrs["unorderedList"] ]; // 의존성을 없애기 위해 명시적으로 정의된 데이터 이름을 가져온다 //var propertyName = attrs["listProperty"]; var propertyExpression = attrs["listProperty"]; if (angular.isArray(data)) { //var listElem = angular.element("
- ");
//element.append(listElem);
// jQuery 의 appendTo 함수로 두줄을 통합
var listElem = angular.element("
- ") // .text(data[i][propertyName])); // 값을 표현식으로 인식하도록 하기 위해서 $eval 함수를 사용 //listElem.append( angular.element("
- ") // .text( scope.$eval(propertyExpression, data[i]) )); var itemElement = angular.element("
- "); listElem.append( itemElement); var watcherFn = function( watchScope) { // 필터 사용을 위해 $eval 함수를 계속 사용한다 // 문제: watcherFn가 호출될 때에는 i가 for 루프를 끝낸 후이다 // ==> 없는 데이터를 참조하므로 공란으로 나온다! return watchScope.$eval( propertyExpression, data[i]); }; // $watch 함수를 이용하여 변경된 newValue가 출력되도록 감시한다 scope.$watch( watcherFn, function( newValue, oldValue) { itemElement.text( newValue); }); } */ for( var i=0; i 정의하는 즉시 호출되는 함수 (또는 자기 실행 함수) // ==> 형식: (function() { ... }()); (function() { //var itemElement = angular.element("
- "); //listElem.append( itemElement); // jQuery 의 appendTo 함수로 두줄을 통합 var itemElement = angular.element("
- ").appendTo(listElem);
var index = i;
var watcherFn = function( watchScope) {
return watchScope.$eval( propertyExpression, data[index]);
};
scope.$watch( watcherFn, function( newValue, oldValue) {
itemElement.text( newValue);
});
}());
}
}
};
})
.controller("defaultCtrl", function($scope) {
$scope.products = [
{ name: "Apples", category: "Fruit", price: 1.20, expiry: 10 },
{ name: "Bananas", category: "Fruit", price: 2.42, expiry: 7 },
{ name: "Pears", category: "Fruit", price: 2.02, expiry: 6 },
{ name: "Tuna", category: "Fish", price: 20.45, expiry: 3 },
{ name: "Salmon", category: "Fish", price: 17.93, expiry: 2 },
{ name: "Trout", category: "Fish", price: 12.93, expiry: 4 },
{ name: "Beer", category: "Drink", price: 2.99, expiry: 365 },
{ name: "Wine", category: "Drink", price: 8.99, expiry: 365 },
{ name: "Whiskey", category: "Drink", price: 45.99, expiry: 365 }
];
// 커스텀 디렉티브를 사용한 경우, 변경된 값이 HTML에 자동으로 반영되지 않는다.
// ==> $watch 가 필요하다!!
$scope.incrementPrices = function() {
for( var i=0; i "+$scope.products[i].price);
}
};
});
<style type="text/css">
</style>
</head>
<body ng-controller="defaultCtrl">
Products
Content will go here</div>Change PricesContent will go here</div> </div> </body> </html>2) jqlite-directives.html
<!DOCTYPE html> <html ng-app="exampleApp"> <head> <title>jqLite - ProAngularJS</title> <link href="/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" /> <link href="/node_modules/bootstrap/dist/css/bootstrap-theme.css" rel="stylesheet" /> /node_modules/angular/angular.js /node_modules/angular/angular-route.js angular.module("exampleApp", []) .directive("demoDirective1", function() { return function( scope, element, attrs) { // children(): DOM 객체의 자식 노드 탐색 함수 // var items = element.children(); // find(): DOM 객체의 모든 자식 노드 탐색 함수 var items = element.find("li"); // get 기능 함수들 // ==> attr(), css(), hasClass(), prop(), text(), val() items.css("color", "red"); // 그 밖에 추가, 수정, 삭제 함수들 // ==> addClass(), removeAttr(), removeClass(), toggleClass() for( var i=0; i"); // 때문에 angular.element로 자식 노드를 만들어 추가한다! var listElem = angular.element("
- ");
element.append( listElem);
for( var i=0; i").append("").text(scope.names[i]);
listElem.append(
angular.element("
- ")
.append( angular.element("").text(scope.names[i]) )
);
}
var buttons = element.find("button");
// on(): 객체에 대한 이벤트 핸들러를 정의
// 이 외의 이벤트 처리 jqLite 메서드: off(), triggerHandler()
buttons.on("click", function(e) {
element.find("li").toggleClass("bold");
});
};
})
.controller("defaultCtrl", function( $scope) {
$scope.names = ["Apples", "Bananas", "Oranges"];
});
<style type="text/css">
.bold { font-weight: bold; }
</style>
</head>
<body ng-controller="defaultCtrl">
<h3>Fruit</h3>
<ol demo-directive1>
<li>Apples</li>
<ul>
<li>Bananas</li>
<li>Cherries</li>
<li>Oranges</li>
</ul>
<li>Oranges</li>
<li>Pears</li>
</ol>
<hr />
Click Me</body> </html>
%d 블로거가 이것을 좋아합니다: - ")
.append( angular.element("").text(scope.names[i]) )
);
}
var buttons = element.find("button");
// on(): 객체에 대한 이벤트 핸들러를 정의
// 이 외의 이벤트 처리 jqLite 메서드: off(), triggerHandler()
buttons.on("click", function(e) {
element.find("li").toggleClass("bold");
});
};
})
.controller("defaultCtrl", function( $scope) {
$scope.names = ["Apples", "Bananas", "Oranges"];
});
<style type="text/css">
.bold { font-weight: bold; }
</style>
</head>
<body ng-controller="defaultCtrl">
<h3>Fruit</h3>
<ol demo-directive1>
<li>Apples</li>
<ul>
<li>Bananas</li>
<li>Cherries</li>
<li>Oranges</li>
</ul>
<li>Oranges</li>
<li>Pears</li>
</ol>
<hr />
- ").appendTo(element);
/*
for( var i=0; i").text(data[i].name));
// 값을 그대로 출력하므로 filter를 사용할 수 없다
//listElem.append( angular.element("