Angular.js는 양방향 바인딩을 지원하기 때문에 컨트롤러나 디렉티브에서 $scope
의 값을 변경해 주면 뷰에서도 변경이 된다. 하지만 이 양방향 바인딩은 Angular.js가 스코프를 인지하고 있을 때만 지원하고 jQuery의 이벤트 리스너 등에서는 스코프를 인지하지 못하므로 scope의 값을 바꾸어도 뷰가 달라지지 않는다.
elem.click(function() {
$scope.test = true;
});
예를 들어 디렉티브에서 위와 같이 코드로 이벤트 리스너를 추가하더라도 스코프의 값은 달라져도 뷰는 바뀌지 않는다.
elem.click(function() {
$scope.$apply(function() {
$scope.test = true;
});
});
그래서 이런 경우는 위처럼 $apply
를 사용해야 제대로 적용이 된다.
하지만 이렇게 사용해서 코딩을 했을 때 컨트롤러와 디렉티브가 복잡하게 얽혀있다 보니 둘의 $apply
가 충돌하는 상황이 발생했다. 어떤 액션이 일어나며서 컨트롤러에서 스코프를 제어하고 있고 컨트롤러에서 발생시킨 동작이 디렉티브까지 이어져서 디렉티브에서도 계속 스코프를 변경하게 된 것이다.(예제를 만들어 볼까 했는데 너무 복잡해 져서..) 그러자 "$apply already in progress"라는 예외가 발생했다. 컨트롤러에서 현재 $apply
가 동작중인데 디렉티브에서 다시 $apply
를 사용하니까 충돌이 난 것이다.
elem.click(function() {
if ($scope.$$phase == '$apply' || $scope.$$phase == '$digest' ) {
$scope.test = true;
} else {
$scope.$apply(function() {
$scope.test = true;
});
}
});
이는 위와 같이 해결할 수 있다. $$phase
로 현재 스코프가 어느 단계에 있는지 알 수 있는데 $$phase
가 $apply
나 $digest
이면 그냥 스코프를 변경하고 그렇지 않으면 $apply
함수를 사용하도록 한 것이다. 이 부분에 관련해서 Angular.js에서 논의도 오간 내용이 있는데 이 논의에 나온 것처럼 safeApply()
같은 함수를 따로 만들어서 사용해도 좋을 것이다.
빨리 Angular.js도 내부 소스를 보기 시작해야 이런 이슈를 쉽게 해결할텐데....
안녕하세요. outsider님!!
최근 angular-summernote가 IE에서 $$phase 오류가 발생되어 수정한 것을 pullrequest 보냈습니다.
Error: [$rootScope:inprog] $digest already in progress
https://github.com/summernote/angular-summernote/pull/34
위에 등록한 링크에서도 scope이 아닌 $rootScope.$$phase를 체크하도록 되어 있네요 ^^
감사합니다.
아 여기도 댓글을 주셨군요.
pull request는 봤는데 저녁정도에 확인해보겠습니다.