Angular1 element를 append할때 함수를 실행하는 directive 만들기

December 06, 2017

이벤트 바인딩의 어려움

Angular1 뿐만 아니라, 모든 javascript app을 개발할때, 이벤트를 바인딩하는 시점을 잡기란 쉽지 않습니다. React처럼 componentDidMount Lifecycle 함수가 존재해서 그 안에서 이벤트 바인딩을 걸어줄 수 있는 것도 아니고, 동적으로 추가되는 DOM element에 jQuery등을 이용해서 이벤트 바인딩 하기란 어렵습니다.

이는, Angular1 Application을 작성할때, element에 이벤트를 주는 부분도 마찬가지 입니다.

그러나, Angular1에는 element에 접근할 수 있는 directive가 존재하므로, directive를 이용해서 위와 같은 사항을 해결해 보고자 합니다.

directive의 link property를 이용한다.

제가 만들고자 하는 directive의 원리는 간단합니다. Angular1에서 directive에는 link라는 property가 존재하며, 이 link라는 property는 directive가 생성될때, 호출할 수 있는 함수입니다. 따라서, link 속성을 이용해서 directive를 정의하고, 실행하고자 하는 element에 directive attribute를 넣어주면, element가 DOM 트리에 구성되어 렌더링될때 해당 function을 실행할 수 있도록 변경해주는 겁니다.

물론 꼼수이기 때문에... 쓰실 분들만 쓰시는 것을 권장합니다 :)

getElementReady directive 만들기

코드를 먼저 보여드리겠습니다.

// getElementReady.js - directive

/**
 * Element가 렌더링 된 후에 function을 호출하는 directive
 * @name getElementReady
 */
angular.module('app')
  .directive('getElementReady', function () {
    return {
      priority: Number.MIN_SAFE_INTEGER,
      link: function ($scope, $element, $attributes) {
        $scope.$eval($attributes.getElementReady);
      }
    };
  });

위의 코드를 보시면, getElementReady라는 directive에 priority속성을 Number의 가장 낮은 숫자로 지정함으로써, compile 되는 순서를 앞으로 놓았습니다. 추가적으로, link function에서 $scope.$eval function을 통해서 element의 attribute 중 get-element-ready attribute에 정의된 함수를 컴파일 하는 원리입니다.

이제, html과 controller를 만듭니다.

// TestCtrl.js
angular.module('app')
  .controller('TestCtrl', function ($scope) {
    $scope.start = function () {
      console.log('element ready!');
    };
  });
<div class="test" get-element-ready="start()"></div>

위와 같이, 실행하시고자 하는 element에 get-element-ready 속성을 주시고, scope에 실행하고자 하는 function을 명시해주면, getElementReady directive에서는 해당 함수를 $eval을 통해서 실행하도록 해줍니다. 이를 통해서 동적으로 element가 append 되는 경우, 이벤트 바인딩을 get-element-ready function에 명시하면, 문제없이 element들이 append 된 후에 이벤트를 바인딩 시켜 줍니다. 다시 말씀드리지만.. 꼼수이므로 쓰실 분들은 변경해서 쓰시면 될 것 같습니다!! :)

...