ProAngularJS Ch15. 커스텀 디렉티브

* 실행 결과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("
      ").appendTo(element); /* for( var i=0; i").text(data[i].name)); // 값을 그대로 출력하므로 filter를 사용할 수 없다 //listElem.append( 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 Prices
      Content 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("
  1. ") .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>

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중

%d 블로거가 이것을 좋아합니다: