toggle menu

[AngularJS] $compile 이란 무엇인가?

2012.11.14 17:29 AngularJS
앵귤러 자체에는 비교적 다양한 API가 제공되지 않기 때문에 보통은 jQuery를 포함시켜 작업을 하게 된다.

전체적인 프레임웍은 앵귤러로 디자인하고 컨트롤러 하부에서 jQuery 등의 외부 라이브러리와 혼용해서 사용하는 방식이다.
먼저 루트프로바이더로 메뉴(혹은 페이지) 구성을 하고, ng-view는 하나만 지원하기 때문에 ng-view 마다 페이지를 읽어들인 후 해당 페이지에 해당하는 컨트롤러에서 jQuery와 연동해서 여러 작업들을 수행한다. 물론 각 페이지에 해당하는 컨트롤러는 루트 프로바이더에서 미리 지정해줄 것이다.

그런데, 종종 컨트롤러에서 ng-view 하나에만 값을 가져오는게 아니라 jQuery의 ajax API를 사용해서 페이지를 읽어와야 할 때가 생긴다.
간단한 자바스크립트가 포함된 HTML 페이지라면 jQuery의 $.ajax() API를 사용해서 충분히 커버가능하지만, 문제는 $.ajax() API를 활용해서 불러오는 HTML 페이지에 앵귤러 표현이 포함되어 있는 경우가 문제가 된다. ng-click 등이 전혀 인식되지 않는 상황!!

이때 사용하는 것이 바로 $compile 서비스다.

간단한 사용예를 살펴보자.

모듈명.controller('컨트롤러명', ['$scope', '$compile' , function($scope, $compile)
{
	//jQuery API인 $.ajax()를 활용해 HTML 페이지를 불러온다.
	$.ajax({
		url:"앵귤러가포함된HTML파일.html",
		cache:'false',
		success: function(data)
		{
			$('#객체ID').html( $compile(data)($scope) );
		}
	}).done(function()
	{
		//완료된 후?
	});
	
	
	//$compile 서비스로 먼저 앵귤러가 인식하도록 컴파일을 한번 해준후,
	//실제 페이지에 추가해준다는 것이 핵심이다!!
	
}


보통은 위에서 $('#객체ID').html(data); 이런 형식으로 바로 데이터를 페이지에 추가해준다.
하지만 이렇게 할 경우 해당 내용을 앵귤러가 인식하지 못하게 된다.

따라서 위의 예와 같이 추가해주기 전에 미리 $compile 서비스를 사용해서 컴파일을 해주어야 한다.
$compile도 서비스이기 때문에 controller 선언시에 등록을 해주어야 하고, $compile 서비스의 파라메터에 컴파일하고자 하는 HTML을 넣게 된다.
옆에 $scope 도 추가로 들어가는데, 이건 현재 컨트롤러의 $scope 상에서 등록이 된다는 의미로 보면 된다.
따라서 저렇게 추가된 페이지의 ng-click 등은 현재 컨트롤러에서 받아서 처리하게 된다.

위에서는 편의상 단 한줄로 처리했지만, 사실 더 정확하게 표현하면 아래와 같이 나타낼 수 있다.

//1단계 : 임의의 HTML 내용을 적용시키기 위해 먼저 HTML을 DOM 요소로 파싱한다.
var template = angular.element('<div>{{name}}</div>');
 
//2단계: 템플릿을 컴파일한다.
var linkFunction = $compile(template);

//3단계: 스코프를 컴파일한 템플릿과 연결한다.
linkFunction($scope);

//4단계: HTML 요소를 반영한다.
element.append( template );


//1-4단계를 한번에 처리
//element.append( $compile('<div>{{name}}</div>)($scope) );

즉, 저 한줄 처리는 $compile 서비스의 리턴값이 link 함수라는 것을 이용해서 즉각 실행패턴을 사용해 바로 $scope와 링크한 것이다.





Forcefully Compile HTML Partials

While working on some integration with existing code, we wanted to have our main app behave normally, and 1 tab work with the new Angular framework. When you click on a link, it would load the code (in all its angular glory) into the page, however how do you get angular to kick in? Originally we tried including script tags into the partial (including a reference to the angular lib itself with ng-autobind) but these appear to be completely ignored by the browser. It appears browsers don't parse async-loaded script tags, need to research and confirm this though.

Instead, we kept the angular script tag in the parent document (to preserve the ng-autobind attribute). When the partial is loaded, we then include the rest of the scripts and call compile() and $apply() to make angular 'kick in' when it otherwise wouldn't automatically. Keep in mind somePartial.htm has the ng-controller attribute. This is the equivalent (seemingly) to using <ng-include> outside of Angular.

// $container is a common jQuery variable naming convention for jQuery-wrapped DOM selectors
var $container = $('#someContainer');
$.ajax({
type: 'GET',
url: 'somePartial.htm',
success: function(data){
$container.html(data);
// Make AngularJS kick in!
angular.compile($container)().$apply();
}
});
Note: if you are calling this inside an Angular controller, you can explicitly pass the scope to the compiler: angular.compile($container)(scope).$apply(); assuming that you did var scope = this;



AngularJS 관련 포스팅 더보기