Skip to content

Angular 환경의 Jquery 사용(Jquery 플러그인) #
Find similar titles

Structured data

Category
Programming

Angular 환경의 jQuery 사용(jQuery 플러그인) #

jQuery 플러그인 #

jQuery는 많은 사람이 사용하기 때문에 그에 맞는 플러그인이 계속 제공되고 있다.
홈페이지에 원하는 기능을 구현하려고 할 때 jQuery 플러그인으로 쉽게 사용할 수 있고 자신만의 플러그인 제작도 가능하다.

    $(document).ready(function() {
        $('#slider').slider({
            slide: function(event, ui) {
                $('#slider-value').text(ui.value);
            }
        });
    });

주요 내용 #

  1. Directive 내 link 함수에서 jQuery와 연동을 할 수 있다.
  2. jQuery로 scope 데이터를 변경하는 경우 자동으로 바인딩 되지 않는다. ( scope.$apply() 호출이 필요 )
  3. 데이터 변경 시 jQuery 쪽으로 호출하는 것도 자동으로 되지 않으므로 scope.$watch()로 지켜보아야 한다.

사용 #

1. Directive 만들기 #

jQuery 플러그인 코드를 재사용하기 위해 플러그인에 대한 directive를 작성할 수 있다. directive는 compile()과 link()로 분리할 수 있는데 compile()은 AngularJS의 template를 변환하는 용도로 사용되고 link()는 model과 view 간의 동적인 연결을 담당한다. 그러므로 모든 플러그인 초기화 코드는 directive의 link 메소드에 있어야 한다.

  • Directive 특징

    • Camel cased name을 가진다. ex) userId
    • 변환될 때는 Snake case(-)로 변경된다. ex) user-id
    • 지시자는 Element name, Attribute, Class name, Comment에 올 수 있다.

      1. Element name : <div test-directive></div>
      2. Attribute : <test-directive></test-directive>
      3. Class name : <div class="test-directive"></div>
      4. Comment : <!-- directive : test-directive -->
      
    • directive는 compile function과 link function으로 나뉜다.

      1. Compile Function 
      : 지시자에 따라 template DOM(the Document Object Model, 문서 객체 모델) element를 변환하여 특별한 DOM Element를 만들어 준다.
      2. Link Function 
      : 지시자가 복제된 DOM Element에 listener를 등록하도록 해준다.
      
  • HTML에서 Directive가 이루어지는 과정

    1. 최초의 Angular의 template는 HTML이므로 표준 브라우저 API를 사용하여 DOM으로 파싱된다.
    2. $compile 메소드를 통해 DOM을 해석하면서 지시자(directive)를 찾는다. 지시자는 $compile() function이 수행되면서 DOM 구조를 변경하고 응답을 위하여 link() function으로 return 한다.
    3. 지시자별로 link()를 호출하여 지시자별 listener 등록과 scope에 대한 watch를 설정한다. 이에 따라 scope와 DOM 사이에 live binding이 되는 것이다.

      var $compile = ...;
      var scope = ...;
      
      var html = '<div ng-bind="exp"></div>';
      
      // Step 1 : parse HTML into DOM element
      var template = angular.element(html);
      
      // Step 2 : compile the template
      var linkFn = $compile(template);
      
      // Step 3 : link the compiled template with the scope.
      linkFn(scope);
      
  • Directive 내부 구성

    • name : 현재 scope의 명칭 (optional)
    • priority : copmpile function의 호출 우선순위를 지정한다.
    • scope : true이면 새로운 scope 객체를 생성하고 부모 scope 객체를 상속하고, false면 새로운 scope 객체를 생성하지 않고 부모가 가진 같은 scope 객체를 공유한다. {} 새로운 격리된 scope 생성으로 부모 scope 상속이 없다. 새로운 컴포넌트 만들 때 유용하다.

      • {}로 만들 때 안의 내용

        @ : directive의 attribute value를 {{ }}방식 (interpolation)을 이용해 부모 scope에 접근
        = : 부모 scope의 property와 directive의 property를 data binding 하여 부모 scope에 접근
        & : parent scope에서 execute expression 방법을 제공
        
    • controller : pre-linking 전에 호출되어서 지시자들끼리 공유를 하려고 할 때 사용

    • require : 다른 controller에 현재 지시자의 linking functon을 전달할 때 사용
    • restrict 속성을 추가하면 일부 메소드에서만 지시문을 호출하도록 제한할 수 있다. 이때 restrict의 값은 E (Element name), A (Attribute), C (Class name), M (Comment)가 올 수 있다.

      <test-directive></test-directive>
      
      <div test-directive></div>
      
      <script>
      var app = angular.module("myApp", []);
      app.directive("testDirective", function() {
          return {
              restrict : "A",
              template : "<h1>Made by a directive!</h1>"
          };
      });
      </script>
      
    • template : HTML file

    • templateUrl : 로딩할 URL 지정
    • replace : true이면 현재 Element를 append가 아닌 replace 한다.
    • transclude : ngTransclued Element로 컴파일 및 지시자로 쓰임
    • compile : compile function 정의
    • link : link function 정의
  • Directive 만들기

    • 전체 내역

      myModule.directive('directiveName', function factory(injectables) {
      var directiveDefinitionObject = {
          priority: 0,
          template: '<div></div>',
          templateUrl: 'directive.html',
          replace: false,
          transclude: false,
          restrict: 'A',
          scope: false,
          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) { ... }
          };
          return directiveDefinitionObject;
      });
      
    • 기본 내역 : 없는 것은 기본값을 사용한다.

      app.directive('ngSliderOne', [function() {
        return {
          restrict: 'A',
          scope: {
            'model': '='
          },
          link: function(scope, elem, attrs) {
            var $slider = $(elem);
            $slider.slider({
              value: +scope.model,
              slide: function(event, ui) {
                scope.$apply(function() {
                  scope.model = ui.value;
                });
              }
            });
          }
        };
      }]);
      

2. Angular App 안에서 jQuery 플러그인 #

Angular App 안에서 jQuery 플러그인을 사용하게 되면 Angular가 플러그인의 변경사항이나 상호작용을 인식하지 못하여 플러그인의 데이터 바인딩이 되지 않는다.

<div ng-app="MyApp">
  <div ng-controller="AppCtrl">
    <div id="ng-slider"></div>
    <p>The slider's value is: </p>
  </div>
</div>

var app = angular.module('MyApp', []);
app.controller('AppCtrl', ['$scope', function($scope) {
  $scope.sliderValue = 0;
  // This slider won't work. Something's missing...
  $('#ng-slider').slider({
    slide: function(event, ui) {
      $scope.sliderValue = ui.value;
    }
  });
}]);

$scope.$apply() #

Angular는 일반 객체를 그대로 사용할 수 있다는 점과 데이터를 원할 때 언제든지 업데이트할 수 있다는 점, 값의 변경이 우리가 설정한 바인딩 객체 안에서 이루어진다는 이점을 가지고 있는데 이 방법이 잘 작동하기 위해서는 데이터가 변경되는 시점이 언제인지 알아야 한다. 이때 $scope.$apply()를 사용하여 끊어진 데이터 바인딩을 복원할 수 있다.

  • 다음은 setTimeout()를 사용하여, 약간의 delay 후 새로운 턴 안에서 함수를 실행하고 있다.

    function Ctrl($scope) {
      $scope.message = "Waiting 2000ms for update";
    
        setTimeout(function () {
            $scope.message = "Timeout called!";
            // AngularJS unaware of update to $scope
        }, 2000);
    }
    

위의 예제에는 Angular가 새로운 순서에 대해서 알지 못하기 때문에, $scope 변경이 화면에 반영되지 않는다. Angular가 변경을 감지하지 못했기 때문인데 우리가 $scope.$apply()로 코드를 감싼다면, $scope 변경을 Angular가 감지하여 화면을 업데이트한다.

  • $scope.$apply()를 사용하여 끊어진 데이터 바인딩을 복원한다.

    function Ctrl($scope) {
      $scope.message = "Waiting 2000ms for update";
    
        setTimeout(function () {
            $scope.$apply(function () { // wrapping using $scope.$apply
                $scope.message = "Timeout called!";
            });
        }, 2000);
    }
    
  • $scope.$apply() VS $scope.$apply(fn)
    $apply 함수를 호출할 때 인수 없이 호출하는 경우와 함수 인수를 넘기고 호출하는 때도 있다. 만약 코드를 함수로 감싸지 않고 $apply 함수에 넘겨주었다면 코드에서 에러가 발생했을 때 그 에러는 Angular의 외부로 던져지게 된다. 즉, application 내부에서 사용하는 에러 처리 메커니즘이 코드가 만들어낸 에러를 발견하지 못한다는 뜻이다. $apply()는 코드를 실행할 뿐 아니라 try/catch문 안에서 실행해주기 때문에 코드에서 발생하는 에러는 항상 Angular에게 잡힌다.

  • $scope.apply()안에서 jQuery 플러그인 사용하기
    $scope.apply() 안에서 실행함으로써 application이 jQuery 플러그인에서 변경한 사항을 모든 $scope 속성에 알린다.

    var app = angular.module('MyApp', []);
    
    app.controller('AppCtrl', ['$scope', function($scope) {
        $scope.sliderValue = 0;
        // This slider WILL work
        $('#ng-slider').slider({
            slide: function(event, ui) {
                // Tell Angular you're updating a value
                $scope.apply(function() {
                    $scope.sliderValue = ui.value;
                });
            }
        });
    }]);
    

Directive 만들기 #

jQuery 플러그인 코드를 더 재사용하기 위해 플러그인에 대한 directive를 작성할 수 있다. 모든 플러그인 초기화 코드는 directive의 link메소드에 있어야 한다.

<div ng-app="MyApp">
  <div ng-controller="AppCtrl">
    <div jq-slider model="sliderValue"></div>
    <p>The slider's value is: <input ng-model="sliderValue"/></p>
  </div>
</div>
var app = angular.module('MyApp', []);
app.controller('AppCtrl', ['$scope', function($scope) {
  $scope.sliderValue = 50;
}]);
app.directive('jqSlider', [function() {
  return {
    restrict: 'A',
    scope: {
      'model': '='
    },
    link: function(scope, elem, attrs) {
      $(elem).slider({
        value: +scope.model,
        slide: function(event, ui) {
          $scope.apply(function() {
            scope.model = ui.value;
          });
        }
      });
    }
  };
}]);

양방향 데이터 추가 ($scope.$watch()) #

$scope.$apply()를 사용하면 jQuery 플러그인에서 model로의 단방향 데이터 바인딩 만을 제공한다. Angular에서는 $scope.$watch()를 사용하여 특정 구문에 리스너를 바인딩하여 model의 변경사항을 볼 수 있다. $watch() 메소드에 검사 할 부분을 알려주고, AngularJS는 검사 중인 항목이 변경될 때마다 청취자를 호출한다.

  • $scope.$watch()
    scope 객체 안에 존재하는 메서드로, 이름처럼 특정 변수를 주시하면서 값의 변화가 있는지를 점검하고 변화가 있으면 미리 등록해놓은 콜백 메서드를 호출하게 된다.

        $scope.$watch('name', function(newValue, oldValue){
        window.alert('$scope.name의 값이 '+ $scope['name']+ '로 바뀌었다.');
        }, true);
    
    1. 첫 번째 파라미터 'name'은 scope 상의 model 이름이다. ($scope.name)
      여러개 인자는; 로 구분한다.
    2. 두 번째 파라미터는 값이 변했을 때 수행하게 될 콜백 메서드이다.
    3. 마지막 파라미터는 true, false로 보통은 값의 변화를 레퍼런스의 변화로 인지하게 된다.
      기본값인 false에서는 관찰하고 있는 변수의 레퍼런스가 변화했는지 체크하게 되고, true일 경우 레퍼런스가 아닌 실제 변수내의 값이 변화했는지를 점검하게 된다.
  • $scope.$watch()로 양방향 데이터 추가
    model이 다른 입력에서 변경될 때 플러그인을 업데이트하려면 $scope.$watch()를 사용하여 값에 대한 변경사항을 Angular로 보고 이러한 변경 사항을 반영하도록 지시문을 업데이트해야 한다.

    var app = angular.module('MyApp', []);
    app.controller('AppCtrl', ['$scope', function($scope) {
      $scope.sliderValue = 50;
    }]);
    app.directive('jqSlider', [function() {
      return {
        restrict: 'A',
        scope: {
          'model': '='
        },
        link: function(scope, elem, attrs) {
          $(elem).slider({
            value: +scope.model,
            slide: function(event, ui) {
              $scope.apply(function() {
                scope.model = ui.value;
              });
            }
          });
          // Watch for changes to the model and update the slider
          scope.$watch('model', function(newVal) {
            $slider.slider('value', newVal);
          });
        }
      };
    }]);
    

참고사이트 #

0.0.1_20140628_0