각도로 응용 프로그램을 구축 한 후 페이지를 내 범위로 수동으로 업데이트해야한다는 것을 알게되었습니다.
내가 아는 유일한 방법 $apply()
은 내 컨트롤러 및 지시문의 범위에서 호출 하는 것입니다. 이것의 문제는 콘솔에 다음과 같은 오류가 계속 발생한다는 것입니다.
오류 : $ digest가 이미 진행 중입니다
누구 든지이 오류를 피하거나 같은 것을 달성하지만 다른 방식으로 달성하는 방법을 알고 있습니까?
답변
이 패턴을 사용하지 마십시오. 이로 인해 해결되는 것보다 더 많은 오류가 발생합니다. 비록 당신이 무언가를 고쳤다 고 생각했지만 그렇지 않았습니다.
를 확인하여 a $digest
가 이미 진행 중인지 확인할 수 있습니다 $scope.$$phase
.
if(!$scope.$$phase) {
//$digest or $apply
}
$scope.$$phase
반환 "$digest"
또는 "$apply"
경우 $digest
또는 $apply
진행 중입니다. 이 주들 사이의 차이점 $digest
은 현재 범위와 그 하위의 시계를 $apply
처리하고 모든 범위의 감시자를 처리한다는 것입니다.
@ dnc253의 요점으로, 전화를 $digest
하거나 $apply
자주 전화를 하면 잘못하고있을 수 있습니다. 나는 일반적으로 Angular의 범위 밖에서 DOM 이벤트가 발생하여 스코프의 상태를 업데이트해야 할 때 요약해야합니다. 예를 들어 트위터 부트 스트랩 모달이 숨겨지는 경우. 때로는 $digest
진행 중일 때 DOM 이벤트가 발생 하지만 때로는 그렇지 않습니다. 그래서이 검사를 사용합니다.
누군가 알고 있다면 더 나은 방법을 알고 싶습니다.
의견에서 : @anddoutoi
- 하지 마십시오 . 호출 스택에서 충분히 높지 않다는
if (!$scope.$$phase) $scope.$apply()
것을 의미합니다$scope.$apply()
.
답변
이 주제에 대한 Angular 사람들과의 최근 토론에서 : 미래 보장을 위해, 당신은 사용해서는 안됩니다$$phase
“올바른”방법으로 눌렀을 때 답은 현재
$timeout(function() {
// anything you want can go here and will safely be run on the next digest.
})
나는 최근에 콜백을받은 페이스 북, 구글 및 트위터 API를 포장하기 위해 각도 서비스를 작성할 때이 문제에 부딪쳤다.
다음은 서비스 내에서의 예입니다. (간단하게하기 위해 변수를 설정하고 $ timeout을 주입하는 등의 나머지 서비스는 중단되었습니다.)
window.gapi.client.load('oauth2', 'v2', function() {
var request = window.gapi.client.oauth2.userinfo.get();
request.execute(function(response) {
// This happens outside of angular land, so wrap it in a timeout
// with an implied apply and blammo, we're in action.
$timeout(function() {
if(typeof(response['error']) !== 'undefined'){
// If the google api sent us an error, reject the promise.
deferred.reject(response);
}else{
// Resolve the promise with the whole response if ok.
deferred.resolve(response);
}
});
});
});
$ timeout에 대한 지연 인수는 선택 사항이며 설정되지 않은 경우 기본값은 0입니다 ( $ timeout 은 $ browser.defer 를 호출 하고 지연이 설정되지 않은 경우 기본값은 0 임)
조금 직관적이지 않지만 Angular를 작성하는 사람들의 대답이므로 충분합니다.
답변
다이제스트주기는 동기식 호출입니다. 브라우저 이벤트 루프가 완료 될 때까지 제어하지 않습니다. 이를 처리하는 몇 가지 방법이 있습니다. 이를 처리하는 가장 쉬운 방법은 내장 $ timeout을 사용하는 것이고, 두 번째 방법은 밑줄이나 lodash를 사용하는 경우 다음을 호출하는 것입니다.
$timeout(function(){
//any code in here will automatically have an apply run afterwards
});
또는 lodash가있는 경우 :
_.defer(function(){$scope.$apply();});
우리는 몇 가지 해결 방법을 시도했으며 모든 컨트롤러, 지시문 및 일부 공장에 $ rootScope를 주입하는 것을 싫어했습니다. 따라서 $ timeout과 _.defer는 지금까지 우리가 가장 좋아했습니다. 이 메소드는 다음 애니메이션 루프까지 기다릴 것을 각도에 성공적으로 지시하여 현재 범위를 적용합니다. $ apply가 끝났습니다.
답변
여기에 나오는 많은 답변에는 좋은 조언이 들어 있지만 혼란을 초래할 수도 있습니다. 간단하게 사용이 $timeout
입니다 하지 최고의이나 적합한 솔루션. 또한 성능이나 확장 성이 염려되는 경우 반드시 읽어보십시오.
알아야 할 것들
-
$$phase
프레임 워크에 대해 비공개이며 이에 대한 충분한 이유가 있습니다. -
$timeout(callback)
현재 다이제스트 사이클 (있는 경우)이 완료 될 때까지 기다린 다음 콜백을 실행 한 다음 끝까지 실행합니다$apply
. -
$timeout(callback, delay, false)
콜백을 실행하기 전에 선택적 지연과 함께 동일한 작업을 수행하지만$apply
Angular 모델 ($ scope)을 수정하지 않은 경우 성능을 저장 하는 (세 번째 인수)를 발생시키지 않습니다. -
$scope.$apply(callback)
는 무엇보다도 호출합니다.$rootScope.$digest
즉, 격리 된 범위 내에 있더라도 응용 프로그램 및 모든 해당 자식의 루트 범위를 다시 정의합니다. -
$scope.$digest()
단순히 모델을 뷰에 동기화하지만 부모 범위를 소화하지 않으므로 HTML의 고립 된 부분을 고립 된 범위 (주로 지시문)로 작업 할 때 많은 성능을 저장할 수 있습니다. $ digest는 콜백을받지 않습니다 : 코드를 실행 한 다음 요약하십시오. -
$scope.$evalAsync(callback)
angularjs 1.2와 함께 도입되었으며 아마도 대부분의 문제를 해결할 것입니다. 이에 대한 자세한 내용은 마지막 단락을 참조하십시오. -
을 얻는다면
$digest already in progress error
아키텍처가 잘못되었습니다. 스코프를 다시 지정할 필요가 없거나 담당하지 않아야합니다. (아래 참조).
코드를 구성하는 방법
해당 오류가 발생하면 스코프가 이미 진행중인 동안 스코프를 다이제스트하려고합니다.이 시점에서 스코프의 상태를 알 수 없으므로 해당 다이제스트를 처리 할 책임이 없습니다.
function editModel() {
$scope.someVar = someVal;
/* Do not apply your scope here since we don't know if that
function is called synchronously from Angular or from an
asynchronous code */
}
// Processed by Angular, for instance called by a ng-click directive
$scope.applyModelSynchronously = function() {
// No need to digest
editModel();
}
// Any kind of asynchronous code, for instance a server request
callServer(function() {
/* That code is not watched nor digested by Angular, thus we
can safely $apply it */
$scope.$apply(editModel);
});
그리고 큰 Angular 응용 프로그램의 일부인 동안 격리 된 작은 지시문에서 수행중인 작업을 알고 있다면 성능을 절약하기 위해 $ apply 대신 $ digest를 선호 할 수 있습니다.
Angularjs 1.2 이후 업데이트
$ scope에 새롭고 강력한 방법이 추가되었습니다. $evalAsync
. 기본적으로 현재 다이제스트 사이클이 발생하면 콜백을 실행합니다. 그렇지 않으면 새 다이제스트 사이클이 콜백을 실행하기 시작합니다.
$scope.$digest
HTML의 분리 된 부분 만 동기화해야한다는 것을 실제로 알고 있다면 여전히 좋지는 않지만 ( $apply
아무 것도 진행되지 않으면 새로운 것이 트리거 되기 때문에 ) 이것은 함수를 실행할 때 가장 좋은 솔루션입니다 어떤 동기 여부를 실행한다면 당신은 그것을 알 수 없다 때때로이 서버에 비동기 호출이 필요합니다 그렇지 않으면 자원이 동 기적으로 로컬로 가져온 것입니다 : 잠재적으로 캐시 된 자원을 가져 오는 한 후 예를 들어,.
이러한 경우 및 귀하가있는 다른 모든 경우, !$scope.$$phase
사용하십시오$scope.$evalAsync( callback )
답변
이 프로세스를 유지하는 편리한 작은 도우미 방법
function safeApply(scope, fn) {
(scope.$$phase || scope.$root.$$phase) ? fn() : scope.$apply(fn);
}
답변
예를 들어 CodeMirror와 같은 타사 스크립트와 Krpano와 같은 문제가 있었고 여기에 언급 된 safeApply 메소드를 사용해도 오류가 해결되지 않았습니다.
그러나 해결 한 것은 $ timeout 서비스를 사용하는 것입니다 (먼저 주입하는 것을 잊지 마십시오).
따라서 다음과 같은 것이 있습니다.
$timeout(function() {
// run my code safely here
})
코드 내부에서 사용중인 경우
이
아마도 팩토리 지시자의 컨트롤러 안에 있거나 바인딩이 필요하기 때문에 다음과 같이 할 것입니다.
.factory('myClass', [
'$timeout',
function($timeout) {
var myClass = function() {};
myClass.prototype.surprise = function() {
// Do something suprising! :D
};
myClass.prototype.beAmazing = function() {
// Here 'this' referes to the current instance of myClass
$timeout(angular.bind(this, function() {
// Run my code safely here and this is not undefined but
// the same as outside of this anonymous function
this.surprise();
}));
}
return new myClass();
}]
)
답변
http://docs.angularjs.org/error/$rootScope:inprog를 참조 하십시오
문제는 호출 $apply
이 때때로 Angular 코드 외부에서 비동기 적으로 실행되고 ($ apply를 사용해야 할 때) 때로는 Angular 코드 내에서 동기식으로 실행될 때 발생합니다 ($digest already in progress
오류 경우에 발생합니다.
예를 들어 서버에서 항목을 비동기식으로 가져 와서 캐시하는 라이브러리가있는 경우 이런 상황이 발생할 수 있습니다. 항목이 처음 요청되면 코드 실행을 차단하지 않도록 비동기식으로 검색됩니다. 그러나 두 번째로 항목은 이미 캐시에 있으므로 동 기적으로 검색 할 수 있습니다.
이 오류를 방지하는 방법은 호출하는 코드가 $apply
비동기 적으로 실행되도록하는 것입니다. $timeout
지연 시간을 0
기본값으로 설정하여 호출 내부에서 코드를 실행하면 됩니다 . 그러나 $ timeout은 자체적으로 다른 주기를 트리거 하여 필요한 모든 업데이트 등을 수행 하기 때문에 내부에서 코드 $timeout
를 호출 $apply
하면 을 호출 할 필요가 없습니다 $digest
.
해결책
간단히 말해서, 이것을하는 대신 :
... your controller code...
$http.get('some/url', function(data){
$scope.$apply(function(){
$scope.mydate = data.mydata;
});
});
... more of your controller code...
이 작업을 수행:
... your controller code...
$http.get('some/url', function(data){
$timeout(function(){
$scope.mydate = data.mydata;
});
});
... more of your controller code...
전화 만 $apply
실행중인 코드를 알고있는 경우 하면 항상 Angular 코드 외부에서 실행됩니다 (예 : $ apply 호출은 Angular 코드 외부의 코드에 의해 호출되는 콜백 내에서 발생 함).
누군가 $timeout
over 사용에 대한 몇 가지 강력한 단점을 알고 $apply
있지 않으면 거의 같은 일을 하기 때문에 왜 $timeout
대신 대신 (제로 지연으로) 사용할 수 없었는지 알 수 없습니다 $apply
.
