이벤트 바인딩의 어려움

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

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

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

제가 만들고자 하는 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 된 후에 이벤트를 바인딩 시켜 줍니다. 다시 말씀드리지만.. 꼼수이므로 쓰실 분들은 변경해서 쓰시면 될 것 같습니다!! :)