Angular: ExpressionChangedAfterItHasBeenCheckedError : ng 4에서 확인한 후 표현식이 변경되었습니다.

에 만든 2017년 06월 16일  ·  201코멘트  ·  출처: angular/angular

제출 중입니다 ...


[ ] Regression (behavior that used to work and stopped working in a new release)
[X ] Bug report #14748 
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

현재 행동


비동기를 사용하여 템플릿에서 관찰 가능한 객체를 사용한 후 다음을 수신합니다.
ERROR 오류 : ExpressionChangedAfterItHasBeenCheckedError : 확인 후 표현식이 변경되었습니다. 이전 값 : ''. 현재 값 : 'something'

예상되는 행동

귀하의 환경에 대해 알려주십시오.

<br i="15"/>
Angular version: 4.2.2

<p i="16">Browser:</p>

<ul i="17">
<li i="18">[X] Chrome (desktop) version Version 58.0.3029.110 (64-bit)</li>
</ul>

<p i="19">For Tooling issues:</p>

<ul i="20">
<li i="21">Node version: v6.10.3</li>
<li i="22">Platform: Mac</li>
</ul>
core regression bufix

가장 유용한 댓글

4.1.3에서 4.2.3으로 업그레이드 한 후에도 동일한 문제가 발생합니다. setTimeout을 사용하면 문제가 해결됩니다.

에서:

PanictUtil.getRequestObservable (). subscribe (data => this.requesting = data);

에:

PanictUtil.getRequestObservable (). subscribe (data => setTimeout (() => this.requesting = data, 0));

모든 201 댓글

redux를 사용하는 것과 동일한 문제가 있습니다. 여기에 내가 그들의 저장소에 게시 한 문제에 대한 링크가 있습니다. 각도 2에서는 오류가 없지만 각도 4.2.2에서는 오류가 확인 된 후 표현식이 변경됩니다.
https://github.com/angular-redux/store/issues/428

4.1.3에서 4.2.x로 이동할 때 내 구성 요소에 문제가 나타납니다. 4.1.3으로 되 돌리면 문제가 해결되지만 영원히 유지하고 싶은 것은 아닙니다.

나는 최소한의 프로젝트에서 재현 할 수 있었지만, 여전히이 회귀 (아마도) 회귀를 수정하는 방법을 찾으려고 노력하고 있습니다. 하지만 dev에서 더티 체크가 시행 된 것 같습니다. 4.2.x에서 모드. 그러나 변경 로그는 그것에 대해 명확하지 않습니다. 모든 팁을 환영합니다.

4.1.3에서 4.2.3으로 업그레이드 한 후에도 동일한 문제가 발생합니다. setTimeout을 사용하면 문제가 해결됩니다.

에서:

PanictUtil.getRequestObservable (). subscribe (data => this.requesting = data);

에:

PanictUtil.getRequestObservable (). subscribe (data => setTimeout (() => this.requesting = data, 0));

@jingglang http://plnkr.co/edit/tpl : AvJOMERrnz94ekVua0u5에서 가능한 최소한의 코드로 문제를 재현 해 주시겠습니까?

동일한 문제가 발생합니다. 2.4에서 4.2로 업그레이드했는데 이제 일부 숨기기 / 표시 논리가 손상되었습니다. 아코디언 스타일의 로직으로, 가시성은 현재 패널을 어떤 패널이 표시되어야 하는지를 보유하는 옵저버 블과 비교하는 데 달려 있습니다. 구성 요소는 @select 를 사용하여 관찰 가능 오고 | 템플릿의 비동기-2.4에서 매력처럼 작동했습니다. 이제 ExpressionChangedAfterItHasBeenCheckedError가 발생하고 패널은 첫 번째 클릭시에만 숨겨지지 않고 표시됩니다.

4.1.3을 4.2.2로 업데이트 할 때 동일한 문제가 발생합니다.

그러나 해결 방법을 찾았습니다.
ChangeDetectionRef 주입하고 오류 지점에서 detectionChanges() 함수를 호출합니다.

constructor(private cdr: ChangeDetectionRef) {
}

onChange(): void {
  this.val += 1; // <- get error after
  this.cdr.detectionChanges();
}

4.2에서 변경된 다음과 같은 문제 라고 생각 합니다.

https://github.com/angular/angular/pull/16592

이 문제를 해결하려면 : https://github.com/angular/angular/issues/14321

ng-content 태그 안에 콘텐츠가 있습니까?

이제 ContentChangedAfter 오류로 오류가 발생하는 ng-content 태그 내의 양식에 동일한 문제가 있습니다.

4.1.3을 4.2.0 이상으로 업데이트 할 때 동일한 문제가 발생합니다.

버그를 재현하기 위해 작은 프로젝트를 첨부합니다.

app-error.zip

이어서 4.1.3으로 다운 그레이드했고 (위에서 언급 한대로) 오류가 사라 지므로 충돌이 무엇이든 4.2에만 해당됩니다. 이번 주 (또는 다음 주)에 plunkr를 구성 할 시간이 없었지만, 스토어에서 두 개의 개별 옵저버 블을 업데이트하는 단일 액션과 관련이있는 것 같습니다. 각각은 UI에 영향을 미칩니다. UI 업데이트 중 하나가 발생하고 다른 하나는 예외를 발생시킵니다.

흠,
버전 4.1.3으로 확실히 롤백하면 문제가 해결되고 @jingglang에 표시된대로 시간 초과가 적용됩니다.

안녕하세요 @tytskyi. 나는이 문제에 대해 plunkr http://plnkr.co/edit/XAxNoV5UcEJOvsAbeLHT 를 만들었습니다. 도움이 되길 바랍니다.

@jingglang이 제공하는 해결 방법은 4.2.0 이상에서 작동합니다.
또는 자식의 생성 이벤트를 생성자로 변경해도 더 이상 오류가 발생하지 않습니다.

@umens 부모 구성 요소를 확인한 후 업데이트하므로 단방향 데이터 흐름을 유지하지 않습니다.

@alexzuza 어떻게 그것을 얻을 수 있습니까? 나는 항상 오류없이 이런 식으로 해왔습니다. SetTimout을 넣어도 더 이상 오류가 발생하지 않는 이유. (나는 plunkr를 업데이트했습니다) 수명주기를 방해하는지 의심 스럽습니까?

안녕 @umens 시간 내 주셔서 감사합니다. 문제는 @alexzuza가 말하는 것입니다. 확인한 후 부모 구성 요소 필드를 업데이트합니다. 귀하의 코드는 대략 다음과 같습니다. http://plnkr.co/edit/ksOdACtXScZKw3VRAYTm?p=preview (코드를 줄이기 위해 서비스를 제거했습니다).
왜 효과가 있었습니까? 아마도 우연히 이전 버전의 angular 또는 Rxjs에 버그가있었습니다. 어떤 버전이 사용되었는지 말씀해 주시겠습니까? 아니면 일하는 플 런커에 넣어?

SetTimout을 넣어도 더 이상 오류가 발생하지 않는 이유.

필드를 비동기 적으로 변경하기 때문에 단방향 데이터 흐름이 적용됩니다.

반대로 생각해 봅시다 : 왜 오류가 발생합니까? 변경 감지의 첫 번째 확인은 위에서 아래로 진행됩니다. 템플릿에 바인딩 된 app.toolsConfig 값을 확인합니다. 그런 다음 렌더링 <child-cmp> 동 기적으로 업데이트하는 app.toolsConfig . 렌더링이 완료되었습니다. 이제 애플리케이션 상태가 안정적인지 확인하기 위해 두 번째 (개발 모드 전용) 변경 감지 검사를 실행합니다. 그러나 자식 렌더링 전 app.toolsConfigapp.toolsConfig 이후와 같지 않습니다. 이 모든 것은 변경 감지의 단일 "턴", "사이클"동안 발생합니다.

확인. 자세한 답변 감사합니다. 하위 구성 요소 수명주기가 언제 발생하는지에 대한 정보를 찾을 수 없습니다.
내가 사용한 이전 "작동"버전의 경우 :
@ angular / * : 4.1.1 (컴파일러 -cli-> 4.1.0 제외)
rxjs : 5.3.1

@umens 여기에 언급 한 이전 버전의 오류가 재현되었습니다. http://plnkr.co/edit/kfoKmigXzFXwOGb2wyT1?p=preview. 그것은 아마도 제 3 자 의존성이 행동에 영향을 주었습니까?

말할 수 없습니다.
더 나아가고 싶다면 여기 내 yarn.lock이 있습니다 : https://pastebin.com/msARLta1
하지만 무엇이 다를 수 있는지 모르겠습니다.

4.1.2 에서 4.2.3 전환 한 후 유사한 "ExpressionChanged ..."오류가 표시됩니다.

제 경우에는 서비스를 통해 상호 작용하는 두 가지 구성 요소가 있습니다. 서비스는 내 메인 모듈을 통해 제공되며 ComponentB 전에 ComponentA 이 생성됩니다.

4.1.2 에서 다음 구문은 오류없이 잘 작동했습니다.

ComponentA 템플릿 :

<ul *ngIf="myService.showList">
...

ComponentB 클래스 :

ngOnInit() {
  this.myService.showList = false; //starts as true in the service
  ...
}

4.2.3 에서 "ExpressionChanged ..."오류를 방지하려면 ComponentA 의 템플릿을 다음과 같이 수정해야합니다.

ComponentA 템플릿 :

<ul [hidden]="!myService.showList">
...

나는 이것이 단지 문제가 된 이유와 *ngIf 에서 [hidden] 로의 전환이 작동하는 이유를 파악하려고 여전히 노력하고 있습니다.

나는 같은 문제가 있으며 주로 다음과 같은 비동기 파이프를 사용할 때 발생합니다.
<div>{{ (obs$ |async).someProperty }}</div>
다음과 같이 사용하면 :

<div *ngIf="obs$ | async as o">
  <div>{{ o.someProperty }}</div>
</div>

그러면 오류가 사라져서 많은 사람들이 이전에 발견되지 않은 실수를 저질렀 기 때문인지 잘 모르겠습니다. 4.2에서 버그가 도입 된 것 같습니다.

나에게도 같은 문제입니다. @ angular / router에서 문제가 발생한 것 같습니다.

또한 라우터와 관련이 있다는 결론에 도달했습니다. 어제 많이 올리려고했지만 실패한 것 같습니다. 더 자세히 조사하고 코드를 단순화하여 오류를 추적 할 수 있는지 확인했습니다. 다음 코드는 작업의 궁극적 인 소스가 패널 헤더 클릭 인 경우 완벽하게 작동하지만 라우팅을 통해 트리거되면 ExpressionChanged 오류와 함께 실패합니다.

<span class="glyphicon pull-right" [ngClass]="{'glyphicon-chevron-up' : (ui$ | async)?.visiblePanels[VisiblePanel.IngredientTypes], 'glyphicon-chevron-down' : !((ui$ | async)?.visiblePanels[VisiblePanel.IngredientTypes])}"></span>

이것은 제가 문제를 해결하는 데 도움

@acidghost 처럼 부모 구성 요소에서 AfterViewInit 에서 ChangeDetectorRef#detectChanges 를 실행하여이 문제를 해결할 수 있습니다.

당신이 plunkr repro를 가지고 있다면 우리는 gitter에 대해 비슷한 불만이 있었고 항상 사용자 문제로 끝났기 때문에 기꺼이 확인하겠습니다.

버그가 4.2에서 도입되었는지 또는 버그가 4.2 미만에서 오류가 부족하여 4.2에서 해결되었는지 확신 할 수 없습니다.

오류 : ExpressionChangedAfterItHasBeenCheckedError
해결책:
componentRef.changeDetectorRef.detectChanges ();

아래는 내 코드 (동적으로 생성 된 구성 요소)의 일부입니다.
componentRef = viewContainerRef.createComponent (componentFactory); // 컴포넌트 생성
(componentRef.instance) .data = input_data; // 여기에서 구성 요소 데이터를 수동으로 변경
componentRef.changeDetectorRef.detectChanges (); // 변경 감지가 발생합니다.

간단한 구성 요소의 경우 "componentRef"에 액세스 한 다음 실행하는 방법을 Google에
componentRef.changeDetectorRef.detectChanges ();
부품 데이터 / 모델 설정 / 변경 후.

해결 방법 2 :
"this.dialog.open (DialogComponent)"대화 상자를 여는 동안 다시이 오류가 발생했습니다.
따라서이 열린 대화 상자 코드를 함수 안에 넣은 다음 해당 함수에 setTimeout ()을 적용하면 문제가 해결되었습니다.
전의:
openDialog () {
let dialogRef = this.dialog.open (DialogComponent);
}
ngOnInit () : void {
setTimeout (() => this.openDialog (), 0);
}
해킹처럼 보이지만 .. 작동합니다 !!!

4.2 이후로 동일한 문제가 있고 해결 방법이나 해결 방법이 없습니다. (나는 ngAfterViewInit 메서드에서 구성 요소 팩토리를 사용하여 동적으로 구성 요소를 만듭니다.이 구성 요소는 @Input() 필드가있는 지시문을 사용합니다. 필드를 정적 문자열로 변환합니다. 지시문의 @Input() 필드는 구성 요소의 뷰가 빌드되고 확인 된 다음 정적 문자열로 초기화 될 때까지 정의되지 않은 것 같습니다.

https://plnkr.co/edit/E7wBQXm8CVnYypuUrPZd?p=info

staticComponent.ts

export class StaticComponent implements AfterViewInit {
  @ViewChild('dynamicContent', {read: ViewContainerRef})
  dynamicContent: ViewContainerRef;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  ngAfterViewInit(): void {
    const componentFactory: ComponentFactory<DynamicComponent> = this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);
    this.dynamicContent.createComponent(componentFactory);
  }
}

test.directive.ts

@Directive({
  selector: '[testDirective]'
})
export class TestDirective {
  @Input()
  testDirective: string;
}

dynamic.component.ts

@Component({
  selector: 'dynamic-component',
  template: `<div [testDirective]="'test'">XXX</div>`
})
export class DynamicComponent {
}

ERROR 오류 : ExpressionChangedAfterItHasBeenCheckedError : 확인 후 표현식이 변경되었습니다. 이전 값 : 'undefined'. 현재 값 : 'test'. 뷰가 부모와 자식이 더티 체크 된 후에 생성 된 것 같습니다. 변경 감지 후크에서 생성 되었습니까?

이 스레드를 읽는 것이 약간 혼란 스럽습니다. 다음 릴리스에서 해결 될 알려진 버그입니까? 아니면 이것이 예상되는 동작입니까? 그렇다면 해결책은 무엇입니까? 감사합니다.

관련이 있는지 아닌지는 모르겠지만 열정적으로이 메시지가 싫어요! 이것은 'ng test'명령을 사용하여 단위 테스트를 할 때만 표시됩니다. 간단한 서비스를 성공적으로 모의하는 방법에 대해 혼란 스러울 수 있다고 생각합니다. 또한 아무도 아직 테스트에서 이것을 보지 못했고 테스트와 관련된 것을 찾을 수 없다는 것에 놀랐습니다. 이 주제와 ExpressionChangedAfterItHasBeenCheckedError + 단위 테스트에 대한 모든 검색을 볼 것입니다.

나는 plnkr를 모 으려고 노력해 왔고 오류를 재현하는 것이 매우 어렵다는 것을 발견했습니다. 그러나 광범위한 디버깅을 통해 무엇이 잘못되었는지 확인하는 데 조금 더 가까워졌습니다 (버그인지 여부를 결정하지는 않지만, 또는 내 실수).

실제 앱에서와 동일한 순서로 결합 된 동일한 감속기의 축소 버전을 사용하여 작은 샘플 앱을 만들었으며 routerReducer는 마지막에 사용했습니다. 실망스럽게도 오류가 발생하지 않았습니다. 그래서 중단 점을 설정하고 감속기를 통해 동작의 흐름을 관찰했습니다. 내가 찾은 것은 두 앱이 역순으로 작업을 전달한다는 것입니다.

내 UI 감속기는 가시성을 관리하고 오류를 유발하는 상태 변경의 원인입니다. 다음은 관련 작업을 보여주는 감속기의 축소 버전입니다.

export const userInterfaceReducer = (state = INITIAL_UI(), action: any) => {
    switch (action.type) {
        case IngredientActions.LIST_INGREDIENTS:
            if (action.hideTypes) {
                return Object.assign(state, { visiblePanel: VisiblePanel.Ingredients });
            }
            return state;
        default:
            return state;
    }
}

이 작은 테스트 앱에서 실행하면 작동합니다. 먼저 LIST_INGREDIENTS 작업이 실행되고 새 상태가 반환됩니다. 그런 다음 @ angular-redux / router :: UPDATE_LOCATION 작업이 실행되고 기본 케이스가 적중되어 동일한 상태를 반환합니다.

실제 앱에서 실행하면 순서 나 동작이 반전됩니다. @ angular-redux / router :: UPDATE_LOCATION 작업이 먼저 실행되어 기본값에서 이전 상태를 반환합니다. 그런 다음 LIST_INGREDIENTS가 실행되어 새 (변경된) 상태를 반환하고 오류가 발생합니다.

나는 내가 멍청한 일을했고 이것은 실제 버그가 아니라는 생각에 완전히 열려 있습니다. 그러나 만약 그렇다면, 다른 사람이 내가 한 일을 볼 수 있습니까?

(각주로 4.1.3 버전을 확인했습니다 ... 그리고 '올바른'순서로 redux 감속기를 실행합니다. 즉, 목록 성분 작업 후에 위치 변경이 발생하므로 아마도 작동하는 이유이지만 4.2 버전은 그렇지 않습니다).

한 구성 요소가 서비스 속성을 변경할 때 동일한 성가신 버그가 발생합니다. * ngIf를 상위 구성 요소로 이동하여 문제가 해결되었습니다. 그래서 확실히 버그입니다

ngDoCheck () 메서드를 사용하고 ChangeDetectorRef.detectChanges 메서드를 호출하여이 문제를 해결할 수있었습니다.

public ngDoCheck(): void {
    this.cdr.detectChanges();
}

@pappacurds 그렇게 하지 마십시오. 그것으로 무한한 변화 감지주기를 일으켰습니다.

여기에 동일한 오류가있을 수 있습니다 https://plnkr.co/edit/IeHrTX0qil17JK3P4GBb?p=preview

오류 : previous undefined after undefiend

업데이트 후 동일한 오류

나도 마찬가지

4.0.0에서 모든 것이 정상이며 4.2.6으로 업그레이드 한 후 동일한 오류가 발생합니다.

수정 사항은 분명하지만 업데이트로 인해 사용자 응용 프로그램이 새로운 부 플랫폼 업데이트를 "지원"하도록 다시 작성되도록 프레임 워크를 변경하는 것은 연결되어 있습니다. JS는 여전히 호환성을 유지하기 위해 오래된 실수를 유지합니다.

여기에 같은 오류

~4.2.4 에서도이 문제가 발생합니다. ~4.1.3 다운 그레이드하면 문제가 해결되었습니다.

ChangeDetectorRef detectChanges() 를 부모의 AfterViewInit 끝에 사용하는 경우에도 ContentChildren 값을 변경하는 데 문제가 있습니다 (변경하는 위치). 이상하게도이 문제는 지시어가 아닌 구성 요소에서만 발생합니다. 4.2.4에서 작업했던 지시문을 <ng-content></ng-content> 템플릿이있는 구성 요소로 변환 한 다음 ExpressionChangedAfterItHasBeenCheckedError 오류가 발생하기 시작했습니다.

재료 2 입력 컨트롤로 작업하는 동안 이것을 얻었습니다. 그것의 가치를 설정하려고

ngAfterViewInit(): void {
this.numberFormControl.setValue(200);
}

ExpressionChangedAfterItHasBeenCheckedError 오류가 Angular 4.2.x 시작해서 만 나타나기 시작한 경우를 보여주는 간단한 Plunker 를 작성했습니다.

상위 구성 요소의 ngAfterViewInit 에서 ChangeDetectorRef detectChanges() 를 사용하더라도 ContentChild 값을 변경할 때 오류를 표시하는 간단한 플 런커 를 만들었습니다.

편집 : 또한 첫 번째 예제를 기반으로 간단한 플 런커 를 만들었지 만 구성 요소 대신 부모 지시문이 있고 오류가 없습니다.

@saverett 귀하의 플렁크 에 다른 오류 메시지가 표시됩니다.

@JSMike 나는 당신의 plunker와 똑같은 경우를 가졌습니다 ( setTimeout , plunker 에서 setTimeout ContentChild 참조 코드를 래핑하여 작업했습니다)

@JSMike 헤드 업 주셔서 감사합니다. angular / zone.js # 832에 문제가있는 것 같습니다. 최신 zone.js를 고칠 때까지 zone.js 버전을 0.8.12로 설정했습니다. Plunker가 다시 작동해야합니다.

@matkokvesic 은 실제로 해결책이 아니며 수명주기 후크 외부에서 변경이 발생하도록합니다. 제공된 코드 예제는 Angular v4.1.3을 사용할 때 오류를 일으키지 않습니다.

@JSMike 동의합니다. 일시적인 해결 방법입니다. 에 의해 참조 요소를 사용하여 ContentChildren 에서 ngAfterViewInit 좋은 사전 각도 4.2.6 일하고 있었다.

https://github.com/angular/angular/issues/17572#issuecomment -311589290에 설명 된 버그에 대한 Plunker를 만들었습니다.

https://plnkr.co/edit/E7wBQXm8CVnYypuUrPZd?p=info

내 코드에서 changeDetection: ChangeDetectionStrategy.OnPushthis.changeDetector.markForCheck(); 하여 문제를 해결했습니다.

@touqeershafi this.changeDetector.markForCheck(); 를 사용한다면 올바른 방법으로 사용하지 않는다는 의미가 아닙니다.

이것은 여전히 ​​일어나고 있습니다. 이것은 예상되는 동작이 아닌 버그이며 수정해야합니다.

@istiti 나는 그것이 적절한 방법이 아니라는 것을 이해하지만 적어도 일시적 으로이 오류를 제거 할 수 있지만 내 프로젝트에 관한 한 이미 버그 URL로 TODO 을 추가했습니다.

이것은 여전히 ​​일어나고 있습니다. 이것은 예상되는 동작이 아닌 버그이며 수정해야합니다.

저는 @rwngallego 에 동의해야한다고 생각합니다 ... 4.2.x 업데이트 이전에 저는 이미 @ViewChildren , @ContentChildren 에 의존하는 재사용 가능한 / ng-content stlye 구성 요소를 했으며 이를 통해 부모와 자식 간의 관계를 생성했습니다. 채널 (속성 가져 오기 / 설정 등).

이 모든 생성자 (이 구성 요소를 확장하는 자식 클래스 포함)에 ChangeDetectorRef 인스턴스를 지속적으로 주입해야하는데 의도하거나 올바른 동작이 아닌 것 같습니다.

@saverett 와 같은 동적 구성 요소를 사용하는 경우 (라우터 사용은 동적 구성 요소를 사용함) 여기에 명시된 문제라고 생각합니다 https://github.com/angular/angular/issues/15634.

ngAfterViewInit / ngAfterViewChecked 내부에서 변경을 수행하는 사람에게는 예상되는 일이라고 생각합니다.

다른 사람들에게는 (아직 여기에서 본 적이 없음) 여전히 나에게 미스터리입니다.

이것은 다음 시나리오에서 저에게 발생합니다.
자식 구성 요소를 사용하는 부모 구성 요소가 있습니다. 자식 구성 요소에는 부모가 제공 할 데이터가 필요하지만이 데이터는 부모 생성자의 약속 결과에서 제공됩니다. 그래서:

부모의

constructor {
dataservice.getData().then((result) => {
this.source = result;
});
}

부모 템플릿

<app-child [source]="source"></app-child>

하위 구성 요소

@Input() source:any[];

이 코드는이 오류를 발생 시키지만이를 상위 구성 요소에 추가하면 문제가 해결되었습니다.

constructor(private cdRef: ChangeDetectorRef) {
dataservice.getData().then((result) => {
this.source = result;
 this.cdRef.detectChanges();
});
    }

이제 이것은 권장되는 솔루션입니까, 아니면 버그에 대한 해결 방법이며 그러한 변경이 의미하는 바는 무엇입니까? 내게는 Angular에게 피해야 할 변화를 감지하라고 말하는 것 같습니다.

@sulhome ... 당신의 문제는 부모 생성자의 약속이 CD 사이클과 개발 모드에서 확인하는 동안 자식 구성 요소에서 @Input 변경된다는 것입니다. 그것이 오류의 원인입니다.

this.cdRef.detectChanges(); 행은 두 번째 CD주기를 추가하므로 성공 후 확인이 완료되고 => 오류가 없습니다.

따라서 모든 것이 예상대로 작동합니다. 이러한 유형의 문제를 방지하려면 코드에서 올바른 구문과 일반 논리를 사용하는 방법을 이해하는 것이 중요합니다. 자세한 설명은 https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4 에서 읽을 수 있습니다.

@ mlc-mlapis 그래서 내가 this.cdRef.detectChanges(); 를 추가하여 제대로하고 있다고 말하고 있으며 이것이 아무런 의미가 없다는 말입니까?
나는 아이의 ngOnChanges들으면서 @Input from parent에서 변경 사항을 선택해야한다고 ngOnChanges 목적이라고 생각했습니다.

@sulhome 예, this.cdRef.detectChanges(); 이 도움이되지만 CD가 2 개 있다는 결과도 있습니다. 따라서 OnPush 전략을 구성 요소에 사용하여 오버 헤드를 제거하는 방법과 위치에 따라 다릅니다. 한 번 더 CD주기.

dataservice.getData 가 http를 사용하여 일부 데이터를 취하는 것이 사실이라면 아마도 가장 쉬운 해결책은 해당 서비스에서 관찰 가능한 것을 가지고 하위 구성 요소에서이를 구독하는 것입니다. 따라서 데이터가 스트림에서 방출 될 때 자식은 이에 반응 할 수 있습니다.

기록을 위해 @ghetolay , 버그 인 경우가 있습니다. 예를 들어 # 18129를 참조하십시오.

@Output을 사용하여 자식 구성 요소에서 데이터를 가져올 때이 문제가 있습니다.

부모 componet home.ts에서

drag_second(trigger){ console.log("dragged222222"+trigger); if(trigger){ this.isactive=true; }else{ this.isactive=false; } }
및 home.html
<div class="upper" [ngClass]="{'isactive':isactive}">

dragstart 및 dragend .... 일 때 isactive가 변경됩니다.

오류 메시지는
ExpressionChangedAfterItHasBeenCheckedError : 확인 후 표현식이 변경되었습니다. 이전 값 : 'true'. 현재 값 : 'false'.

또는 클래스 속성을 변경 / 추가 할 수있는 다른 방법이 있습니까?

4.2.x 및 4.3.0에서는 동일하지만 4.1.x에서는 그렇지 않습니다.

ng-template 를 사용하는 경로에서만 오류가 발생합니다.

해결 방법 : 모든 코드를 넣기 ngAfterViewInitsetTimeout (@jingglang 덕분에 / 학점) 나를 위해 문제를 해결했다.

public ngAfterViewInit(): void {
    setTimeout(() => {
        //my code
    });
}

@Wernerson , setTimeout에 대한 JSMike의 의견을 살펴보십시오. 실제로 라이프 사이클 후크 외부에서 코드를 변경하고 있다는 점에 유의하는 것이 중요합니다.

내 문제는 ActivatedRouteparams Observable 을 (를) 사용하여 ngOnInit() 일부 주를 수정했다는 것입니다. Routerparams 동기화를 방출하는 것으로 보이므로 CD 중에 업데이트가 발생합니다.
이에 대한 내 간단한 해결책은 현재 다음과 같습니다.

  // !!! see delay(0)
  // results are consumed by async Pipe, where I also had the CD issue (null vs. undefined)
  this.results = this._activatedRoute.params.delay(0)
    .do(params => this.searchParameters = this._createSearchParameters(params))
    .switchMap(p => {
      let key = this.searchParameters.key();
      return this._searchResultCache.results.map(r => r.get(key));
    });

(나는 이것이 지금까지 가장 아름다운 반응 / 기능 코드가 아니라는 것을 알고 있습니다.))
그래서 해결책은 delay(0) 매개 변수 방출을 tick 입니다.

비슷한 문제가있는 다른 사람들을 도울 수 있기를 바랍니다.
또한 내 솔루션이 어떻게 잘못되었거나 위험한 지에 대한 우려도 환영합니다.

이 문제의 상태는 어떻습니까? Angular 4.2.6에서는 여전히 분명합니다.
프로덕션 빌드에 문제가 있습니까? 이 오류는 디버그 빌드에서만 발생합니다.

수동으로 변경 감지를 실행하거나 setTimeout ()에서 실행하는 모든 해결 방법이 싫습니다.

setTimeout() 는 전체 응용 프로그램에서 새로운 변경 감지주기를 유발하는 반면 detectChanges() 는 현재 구성 요소 AFAIK 만 확인하므로 비효율적입니다.

우리 팀은 ngOnInit이 무엇인지 아닌지에 대해 핵심적인 오해를 가졌습니다. 문서는 말한다

ngOnInit() : Angular가 데이터 바인딩 된 속성을 먼저 표시하고 디렉티브 / 컴포넌트의 입력 속성을 설정 한 후 디렉티브 / 컴포넌트를 초기화합니다. 첫 번째 ngOnChanges () 이후 한 번 호출됩니다.

우리는 ngOnInit 에서 (애플리케이션 상태를 추적하는) 서비스를 구독하고있었습니다. 구독은 동기식 BehaviorSubjects입니다. 구독 시간에 실행됩니다. 따라서 "Angular가 먼저 지시문 / 구성 요소의 입력 속성을 설정 한 후"값을 즉시 변경했습니다. 여기에 문제가 있음을 보여주는 매우 간단한 Plunker가 있습니다.

Tour Of Heroes의 예제는 궁극적으로 비동기식 http를 사용하므로 문제를 일으키지 않습니다. 이것이 우리의 혼란의 원인이라고 생각합니다.

지금 우리가하는 일은 생성자의 동기 상태를 대신 구독하여 구성 요소의 값이 즉시 설정되도록하는 것입니다. 이 스레드의 다른 사람들은 타임 아웃을 통해 두 번째 변경 감지를 시작하거나 나중에 값을 설정하거나 명시 적으로 angular에게 그렇게하도록 지시했습니다. 그렇게하면 오류가 나타나지 않지만 imho는 아마도 불필요합니다.

@maximusk가 공유 한 기사는 반드시 읽어야합니다 : ExpressionChangedAfterItHasBeenCheckedError 오류에 대해 알아야 할 모든 것 .

다른 것이 없다면이 스레드는 공식 각도 문서에서이 모든 것에 대한 심층적 인 경고를 표시해야합니다.

@maximusk가 공유 한 기사는 반드시 읽어야합니다 : ExpressionChangedAfterItHasBeenCheckedError 오류에 대해 알아야 할 모든 것.

감사합니다

뿡뿡

ngAfterViewInit () 라이프 사이클이 실행될 때까지 자식을 사용할 수 없기 때문에 @ViewChildren @ContentChildren 을 사용하여 부모에서 자식 속성을 수정하는 경우에 작동합니까?

이 문제의 일부는 확인 된 버그이며 사용자 잘못이 아닙니다.
https://github.com/angular/angular/issues/15634

@saverett가 해결 한 문제는 정확합니다. *ngIf 를 사용하면 오류가 발생했습니다.

*보고있다...

이것은 객체를 Angular Material 대화 상자에 전달한 다음 대화 상자에서 this.myDataInput = Object.assign({}, dialogDataInput) 수행하는 대신 직접 사용하는 경우 발생할 수 있습니다. 즉, this.myDataInput = dialogDataInput; 다음 this.myDataInput.isDefault = !this.myDataInput.isDefault; 와 같은 작업을 수행합니다. 위에서 설명한 것처럼 하위 구성 요소 (대화 상자)는 상위 구성 요소의보기에 표시되는 값을 변경합니다.

속성을 [속성]으로 대체

누군가 이것이 의도 된 오류인지 버그인지 확실히 알고 있습니까?
ngOnInit에서 데이터를로드하는 것이 맞지 않습니까? 코드를 생성자로 이동하면 (Observable을 통해로드) 오류가 발생하지 않기 때문입니다.

@Wernerson . HTML에 * ngIf가 있습니까? ngOnInit 가 완료된 후에 모든 것이 렌더링 될 것이라는 점에 유의하십시오. * ngIf 내부의 모든 것이 작동하지 않고이 오류가 발생할 수 있습니다.

다시 작업해야합니다. "ngIf"및 사용자 지정 컨트롤을 사용하면이 오류 메시지가 표시됩니다. 타임 아웃은 확실히 해결책이 아닙니다.

@ j-nord 댓글을 읽으면 timeout 가 필요하지 않다는 것을 알 것입니다.

@ j-nord / @dgroh 저도 같은 경험을했습니다. ngIf 지시문을 사용하는 ng-hint를 사용하고 있는데이 오류를 제거 할 수 없습니다. 해결 방법을 구현 했습니까?

@Wernerson 예,이 문제의 일부는 의도 된 오류 또는 버그입니다 : https://github.com/angular/angular/issues/15634

@ mawo87 그것은 DOM에서 요소를 제거하지 않기 때문에 숨김 으로 나를 위해 일했습니다. 우리의 요구 사항에는 괜찮 았고 깨끗한 솔루션이었습니다.

뿡뿡
먼저 오류 메시지가 의미하는 변수를 찾아야합니다. @Angular Team : 오류 메시지에 변수 이름이 있으면 좋을 것 같습니다. 이것은 더 큰 마스크와 Webpack으로 찾기가 쉽지 않습니다. 명령이 발생한 위치를 찾은 경우 다음 줄의 명령이 도움이 될 것입니다.

this.cdr.detectionChanges();

19. June의 vermilion928 의 예를 참조하십시오

@dgroh 와 @ j-nord에게 감사드립니다.
@ j-nord 또한 detectionChanges 메서드를 살펴보고 있었지만 다음 기사에서와 같이이 접근 방식을 사용하지 않을 수도 있습니다.
https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

제 경우에는 PageTitle 서비스를 사용하고 부모 구성 요소에서 구독하고 있으며 자식 구성 요소가 뷰에로드되면 서비스에서 PageTitle을 설정하고 부모는 값을 가져와 구독을 통해 표시합니다. 4.1.x에서는 오류없이 작동했지만 4.2x 이상에서는 ExpressionChangedAfterItHasBeenCheckedError 오류가 발생합니다.

모든 것이 여전히 예상대로 작동하고 배포 / 프로덕션 빌드에서는 오류가 표시되지 않지만 (두 번째 변경 감지주기가 없기 때문에) 개발 중에는 콘솔에 엄청나게 많은 빨간색이 있습니다.

나는 여전히 적절한 (단기간이라도) 수정이 무엇인지에 대해 극도로 불분명합니다. 내 부모 구성 요소 구독은 생성자에 있고 서비스를 통한 자식 구성 요소 업데이트는 ngOnInit에 있습니다.

나는 '당신이 알아야 할 모든 것 ...'게시물을 읽었는데, 그것은 유익했지만 내가 잘못하고 있다고 믿게 만들지 않으며 문제에 대한 해결책을 제공하지 않습니다.

@alignsoft ngOnInit 의 하위 구성 요소에 페이지 제목을 설정하면 오류가 유효하다고 생각합니다 ... 이전에 링크 된 기사를 참조하십시오.

@alignsoft 동의합니다.이 오류에 대해 많은 논의가 있었지만 그 유효성 여부에 대한 명확한 지침이 없었습니다. 이것이 표준 및 데이터 바인딩 / 조작에 대해 개발자에게 "경고"에 더 가깝습니까?

내 특정 경우에는 @ViewChild@ContentChild를 사용하여 부모 / 자식 구성 요소를 다루고 있으며, detectChanges ()를 반복해서 호출하여 콘솔에 오류가 표시되지 않도록해야합니다.

@rolandoldengarm 부모 생성자에서 구독을 시도하고 자식 구성 요소에서 AfterViewInit의 값을 설정했지만 여전히 오류가 발생합니다. 이것이 흐름 및 변경 감지주기를 기반으로이를 처리하는 올바른 방법이며 오류가 잠재적으로 버그일까요?

제발, 여기에 공식적인 답변이 있으면 좋을 것입니다 ...

이걸 다시보세요. https://github.com/angular/angular/issues/17572#issuecomment -315096085의 내 예처럼 보입니다.
구성 요소 수명주기의 일부로 ContentChildren을 조작하려고 할 때 AfterViewInit 대신 AfterContentInit 를 사용해야합니다.

다음은 AfterContentInit로 업데이트 된 plnkr이며 더 이상 오류가 없습니다. http://plnkr.co/edit/rkjTmqclHmqmpuwlD0Y9?p=preview

@JSMike 나는 AfterViewInitAfterContentInit 로 변경했지만 여전히 동일한 오류입니다. OnInit 내부의 데이터를 업데이트하더라도 오류가 발생합니다. 내가 아는 한 이것은 Angular 수명주기에 따라 OnInit 동안 데이터를 초기화하는 올바른 (맞습니까?)입니다. 이 오류가 이전 버전에서 발생하지 않았다는 사실과 함께 이것이 버그라고 가정합니다. 하지만 만약 그렇다면 개발팀으로부터 어떤 종류의 성명을 받고 싶습니다 ..? :디

@Wernerson 데이터를 해당 수명주기 후크에서 사용할 수없는 경우 ngOnInit 내부의 데이터를 업데이트해서는 안됩니다. 콘텐츠 하위와 관련이있는 경우 논리를 ngAfterContentInit 해야합니다.

@JSMike 도움이되지 ngAfterContentInit 에서 (비동기 적으로) 요청 된 데이터를받은 후 애플리케이션의 상태 변경을 구독합니다.

@Wernerson plnkr 예제가 있습니까?

StackBlitz
이벤트를 생성하는 자식 구성 요소와 로컬 변수를 업데이트하는 부모 구성 요소입니다.
js 콘솔에서 오류가 발생합니다.
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'hello world'.

ngAfterContentInit 는 자식이 정적 인 경우에만 작동하지만 *ngFor http://plnkr.co/edit/rkjTmqclHmqmpuwlD0Y9?p=preview를 사용하여 자식을 만들 때 여전히 실패합니다

@soldiertt 당신은 detectChanges ()가 필요합니다 https://stackblitz.com/edit/angular-qfu74y

@soldiertt 이상하게 보이지만보고해야 할 별도의 문제처럼 보입니다.

@JSMike 내 설정은 서비스, 타사 라이브러리, 기밀 정보 등으로 상당히 복잡합니다. 불행히도 플 런커에서 버그를 재현 할 수 없습니다.

ChangeDetectorRef 주입이 저에게 효과적이었습니다!

캐 러셀에이 문제가 있는데, 캐 러셀 항목 컴포넌트에 속성을 설정해야하므로 상위 캐 러셀 컴포넌트에서 계산 된 후에 만 ​​알려진 속성입니다. 그리고 모든 항목이 ng-content 를 통해 전달되므로 상위 캐 러셀 구성 요소의 ngAfterContentInit 에있는 항목으로 만 작업 할 수 있습니다. 다음과 같은 것 :
캐 러셀 :

@ContentChildren(ItemComponent) carouselItems;

ngAfterContentInit() {
  // ... some computations
  this.carouselItems.forEach((item: ItemComponent, currentIndex) => {
    item.setPropertyX(someComputedValue);
    // OR:
    item.x = someComputedValue; // of course, this would work as well
  });
}

안건:

setPropertyX(value) {
  this.x = value;
}

setTimeout() 해킹 외에 유일한 다른 해결책은 항목이 상위 캐 러셀과 통신하도록 서비스 및 주제를 사용하는 것입니까? 말도 안 돼요 ... 즉, 캐 러셀에 항목이 100 개 있으면 구독이 100 개가됩니다. 2 개의 캐 러셀이 있으면 두 배가됩니다. 당연히 추천하는 방법이 아닐까요?! 권리?

대부분의 경우 구성 요소에 몇 가지 속성, AfterContentInit 이전에는 알 수없는 속성을 설정해야합니다. 그리고 많은 경우 (다음과 같은 1 ~ 2 개의 구성 요소에 대해 이야기하지 않을 때) 하지만 회전 목마처럼 50 개 또는 100 개) 구독이 10 분의 1 또는 수백이면 말도 안됩니다.

여기에서 나를 도울 수있는 Angular의 정말 멋진 부분이 있지만 아직 발견하지 못한 부분이 있습니까?

추신 나는 또한 ChangeDetectorRef 를 주입하고 캐 러셀 구성 요소와 항목 구성 요소 모두에 detach() 를 사용하려고 시도했습니다. 나는 이것이 오류를 수정하기를 기대했을 것입니다. 음 ... 기본적으로 "_screw change detection, 나는 실제로는 전혀 필요하지 않습니다. 제발, 제발 : 변경 사항을 감지하지 마십시오 _"... 그러나 오류는 여전히 발생합니다! 그렇다면 detach() 메서드가 말하는대로 작동하지 않는다면 요점은 무엇일까요?

constructor(private cdr: ChangeDetectorRef) {
  this.cdr.detach();
}

setTimeout() 은 (는) 해킹 이 필요하지 않습니다. 예를 들어보기에 반응하여 업데이트 할 때 예 : ngAfterViewInit 동안 템플릿의 바인딩 변경과 같은 작업을 수행하는 매우 유효한 방법 일 수 있습니다.

문제는 그 이유를 모르고 마법으로 사용할 때입니다. 그러나 setTimeout() 를 사용하는 것이 적절한 방법 인 완전히 유효한 시나리오가 있습니다.

@ghetolay 변경 감지 시스템이 평가하는 구성 요소의 수명주기에서 벗어나는 것이 해키하지 않습니까? 나는 ChangeDetectorRef.detectChanges () 경로를 사용했지만, 나에게는 해킹처럼 느껴집니다. 이러한 솔루션 중 하나가 "용인"이라면 우리는 함께 살아야한다고 생각하지만 실제로는 ViewChild (ren) 또는 ContentChild (ren) 수정 사용

뿡뿡

변경 감지 시스템이 평가하는 구성 요소의 수명주기에서 벗어나는 것이 해키하지 않습니까?

나는 그 질문을 이해하지 못합니다. 수명주기에서 벗어나?

Angular는 비동기 이벤트 (dom 이벤트, xhr, setTimeout 등)가 발생할 때마다 CD 라운드를 실행하고 해당 CD 동안 뷰를 업데이트합니다. ngAfterViewInit 후크는 그 후에 실행되므로 더 이상 실행할 CD가 없으며 다른 비동기 이벤트가 발생할 때까지 다시 트리거되지 않습니다. 다시 트리거하는 가장 간단한 방법은 setTimeout() 를 사용하는 것입니다. 확실히 스스로 할 수는 있지만 이점은 무시할 수 있다고 생각합니다.

내가 @JSMike과 내가 읽은 다른 스레드에서 주석을 참조하고 @ghetolay :

@matkokvesic 은 실제로 해결책이 아니며 수명주기 후크 외부에서 변경이 발생하도록합니다. 제공된 코드 예제는 Angular v4.1.3을 사용할 때 오류를 일으키지 않습니다.

그래서 나는 setTimeout ()을 사용하면 수명주기 후크가 "건너 뛰기"되거나 평상시처럼 평가되지 않을 것이라고 생각합니다.

@ghetolay @fugwenna ChangeDetectorRefdetach() 방법은 어떻습니까? 이견있는 사람? 변경 감지를 완전히 비활성화하여 오류도 제거해야하지 않습니까? 왜 작동하지 않는지 이해가 안 되나요? 아니면 "독립적 인"오류가 발생합니까? Angular는 우리가 변경 감지를 비활성화했다는 사실에 대해 신경 쓰지 않는다는 것을 의미합니다. 속성이 설정된 후 변경된 것만 신경 쓰는 것입니까?
만약 사람들이 여전히 "ExpressionChangedAfterItHasBeenChecked"가 버그인지 아닌지에 대해 논쟁한다면, 이것은 확실히 버그 일 것입니다 ... 제 생각에 Angular는 속성이 설정되면 더 이상 우리가 속성을 변경하는 것을 신경 쓰지 않을 것입니다. 비활성화 된 변경 감지. 특히 내가 한 방식, 부모 구성 요소와 다른 모든 자식 구성 요소에 대해 기본적으로 전체 트리에서.

나는 여기서 약간 길을 잃었다. 내가 가진 문제 중 하나는 개발 모드에서 예외를 트리거하지 않고 매우 일반적인 일을 어떻게할지 확신하지 못한다는 것입니다.

예를 들어 datepicker를 캡슐화하는 제어 값 접근 자 구성 요소를 작성했습니다. 이 구성 요소에 내부적으로 datepicker에 전달되는 빈 모델을 제공하여 초기화하고 등록 된 함수를 통해 다시 보냅니다. 물론 아이들은 초기 단계에서 모델을 수정하지만 그게 내가 원하는 것입니다! 초기화 된 모델을 되돌리고 싶지만 그 방법을 이해하는 것은 datepicker이기 때문에 부모에서 초기화를 수행하지 않습니다.

문제는 지금 어떻게해야합니까? setTimeout을 호출하고 변경 감지를 다시 트리거 하시겠습니까? ChangeDetectorRef를 사용 하시겠습니까? 코드를 읽기 어렵게 만들기 때문에 마음에 들지 않습니다. 분리 된 서비스에서 초기화 하시겠습니까? 그러나 그 서비스는 비즈니스 가치가없는 매우 기술적 목적에 불과하며 이것이 문제를 해결할 수 있을지조차 확신하지 못합니다.

@fugwenna 내 주요 관심사는 4.1.3에서 작동했으며 이제 손상되었다는 것입니다. init 함수에서 setTimeout 를 사용하고 영역이 인계되도록해야하는 경우 각도 팀의 누구도 아직 차임하는 것을 보지 못했지만 이전에 작동했던 방식에 비해 안티 패턴처럼 느껴집니다.

@ kavi87 그래서이 stackoverflow 질문은 실제로 필요한 우아한 솔루션을 제공했습니다. 내 자식 구성 요소의 이벤트 이미 터를 비동기 로 만드는 것이 었습니다.
new EventEmitter(true)

109 개의 댓글이있는 회귀로 분류 된 문제의 경우이 문제는 공식적인 단어로는 실망스럽게도 조용합니다.

여기에있는 모든 사람들이자가 진단 기술을 잘 사용하고 있지만 이것이 실제로 버그인지 아니면 4.2.x 이후에 구현 된 새로운 동작인지 알고 싶습니다. 그렇다면 주요 변경 사항으로 나열되어야합니다 (따라서 부 릴리스가 아닙니까?).

뿡뿡
예외를 침묵시키기 위해 변경 감지를 완전히 비활성화하는 것이 이론 상으로는 어떻게 이해되어야하는지 / 이해할 수 있는지는 알지만, 좋은 방법은 아닙니다.

나는이 오류 메시지가 변경 사항에 대한 문서화없이 4.2x 이후 에 발생한 이유에 대한 응답이나 설명이 거의 @JSMike@rosslavery 에 동의합니다. 거의 변화 감지를 방해하는 경고처럼 느껴지는 개발 모드 (?)

Im 내 AppComponent에서 div가 있습니다.
해당 div의 유일한 목적은 바쁜 오버레이를 표시하는 것입니다.
사용 중 상태는 http 서비스 클래스 내에서 트리거됩니다.

app.component.html

<!-- Busy Overlay -->
<div *ngIf="busy$ | async"
     class="overlay">
    <i class="loading">
        <i><sk-wave></sk-wave></i>
    </i>
</div>

app.component.ts :

  get busy$(): Observable<boolean> {
    return this.catolService.busy$;
  }

4.3.4로 업그레이드 한 후 서비스가 사용 중 상태를 트리거 할 때 항상 ExpressionChangedAfterItHasBeenCheckedError가 발생합니다.

이 간단한 비동기 시나리오에 대한 수정 사항이 있습니까?

@ emazv72 ...이 stackblitz / plunker 데모가 귀하의 경우와 동일합니까? https://stackblitz.com/edit/angular-2p2h9s?file=app%2Fapp.component.ts 또는 https://plnkr.co/edit/RPOxSVnXvM1TqKp0peSY?p=preview

@mukunku 감사합니다.

@ mlc-mlapis 내 코드가 정확히 https://plnkr.co/edit/uUT7tiM5BKPIy2upJPhb?p=preview입니다.

하지만 플 런커는 문제를 보여주지 않습니다. 최신 4.x로 업그레이드하여 수정되었는지 확인하겠습니다.

감사

안녕하세요, 지연에 대해 사과드립니다.

그래서-이 동작은 실제로 의도 한대로 작동하고 있으며, 일부 사람들에게는 예상치 못한 일이 될 수 있다는 것을 알고 있으므로 메커니즘을 설명해 보겠습니다.

@saveretts 'plunker 통해 걷는이의 좋은의 생식입니다 (감사합니다!) - https://plnkr.co/edit/fr7rTNTCgEhHcbKnxAWN?p=preview

tldr은 Angular가 항상 이런 방식으로 작동했습니다. 변경 감지는 한 번의 패스로 위에서 아래로 (또는 부모-> 자식) 실행됩니다. 따라서 다음과 같은 경우 :

@Component({ 
  selector: 'app-root',
  template: `
    <!-- initially falsey -->
    <div *ngIf="sharedService.someValue">NgIf Block</div>
    <child-comp></child-comp>
  `
})
class AppRoot {
  constructor(public sharedService:SharedService){}
}
@Component({ 
  selector: 'child-component',
  template: `child comp`
})
class ChildComponent {
  constructor(public sharedService:SharedService){}
  ngOnInit(){
    this.sharedService.someValue = true;
  }
}

변경 감지는 먼저 app-root에 대해 실행되고 모든 바인딩을 확인한 다음 하위 구성 요소를 변경 감지합니다. 다시 한 번이 작업을 단일 패스 (AngularJS 스타일이 안정 될 때까지 계속 확인하는 대신)로 수행하므로 부모 바인딩입니다. (이 경우 * ngIf)는 다시 검사되지 않으므로 이제 응용 프로그램이 일관되지 않은 상태 (!)

개발 모드에서는 실제로 변경 감지를 두 번 실행합니다. 두 번째 실행시 바인딩이 변경되지 않았는지 확인하고 (일관되지 않은 상태를 나타냄) 이러한 문제가 발견되면 오류가 발생합니다. 재현에서 enableProdMode() 를 호출하여이 동작을 확인할 수 있습니다. https://plnkr.co/edit/GGEzdxK1eHzHyGiAdp56?p=preview. 오류는 발생하지 않지만 (CD x2를 실행하고 있지 않기 때문에) 응용 프로그램은 불일치 상태 (하위는 표시되지만 * ngIf는 표시되지 않음)로 끝납니다. 이는 잘못된 것입니다.

이것이 최근에 나타난 이유는 이전에 라우터가 구성 요소를 삽입 할 때 여러 가지 추가 변경 감지 검사를 실행했기 때문입니다. 이것은 비효율적이었고 특히 라우터 콘센트가 ngIfs 등에 중첩 된 경우 애니메이션 및 라우팅에 문제를 일으켰습니다 이 동작이 수정되어 위와 같이 이미 잘못된 코드 가 개발자가 단방향 데이터 흐름을 위반하고 있다는 오류를

일반적으로 위의 재현처럼이 단방향 흐름을 위반하는 코드는 올바르지 않습니다 . 그러나-이 동작을 원할 수있는 경우가 있습니다 (사실 Angular 양식 라이브러리에 유사한 설정이 있습니다). 이것이 의미하는 바는 다른 변경 감지 실행을 명시 적으로 트리거해야한다는 것입니다 (루트 구성 요소로 돌아가서 하향식 확인). setTimeout 호출하면이 작업을 수행 할 수 있지만 일반적으로 Promise를 사용하여 트리거하는 것이 더 좋습니다. https://plnkr.co/edit/IoKMxEOlY9lY9LcWwFKv?p=preview 참조

도움이 되었기를 바랍니다.

@robwormald 이 같은 오류는 다른 시나리오에서도 발생했습니다. 이것이 ContentChildren 문제를 해결하는지 확실하지 않습니다 . 지시문이 AfterContentInit 중에 오류없이 자식 값을 변경할 수 있지만 구성 요소에서 동일한 작업을 수행하면 오류가 발생하는 이유는 무엇입니까?

또한 한 init 프로세스 중에 cdr.detectChanges() 가 호출되면 다른 init 함수가 호출되지 않을 것으로 예상됩니까? 이것은 내가 별도의 문제를 만든 이 스레드에서 발견 된입니다 .

뿡 빵뀨

ContentChild (ren) 및 ViewChild (ren)를 사용하여 단방향 흐름이 Parent-> Child에서 오는 시나리오에서이 오류가 여전히 발생한다는 평가에서 @JSMike 가 정확하다고 생각합니다.

이전하는 것이 허용됩니까? 즉,이 오류는 개발자가 단방향 흐름 "모범 사례"에 반대하지만 변경 감지 이후 응용 프로그램 중단 오류를 일으키지 않음을 나타내는 "경고"에 가깝습니다. 프로덕션 모드 (실제 세계)에서 한 번만 실행됩니까?

@fugwenna 아니요, 그렇지 않으면보기가 일관성없는 상태로 유지되므로 실제로는 오류입니다. Rob이 실행중인 샘플이 모든 것을 알려줍니다.

또한 문서 에서이 인용문이 있습니다.

Angular의 단방향 데이터 흐름 규칙은 뷰가 구성된 후 업데이트를 금지합니다. 이 두 가지 후크는 구성 요소의 뷰가 구성된 후에 실행됩니다.

실제로 변경해야하는 경우 비동기 메서드 중 하나를 사용하여 변경하십시오. setTimeout 이 여기에 적합 할 수 있지만 (이 글을 쓰는 시점에서) 자체적으로 4ms 지연이 발생한다는 점에 유의하십시오. 다른 방법은 Promise.resolve().then(() => Assignment = here)

더 나은 방법은 한 발 물러서서 프로세스 후반에 변경해야하는 이유를 확인하고 문제를 해결할 수있는 더 깨끗한 방법이 있는지 확인하는 것입니다. 내가이 일을하는 것을 발견하면 코드 냄새로 취급합니다.

@robwormald에 대한 자세한 설명에 감사드립니다. *ngIf 대신 [hidden] 바인딩을 사용할 때 자식-> 부모 데이터 흐름이 여전히 작동하는 것처럼 보이는 이유가 여전히 혼란 스럽습니다 (https://plnkr.co/edit/L4QWK5pTgF1qZatFAN4u?p 참조). = 미리보기). ExpressionChangedAfterItHasBeenCheckedError 없이 왜 이것이 여전히 작동하는지 아이디어가 있습니까?

Angular는 checkAndUpdateView 함수를 사용하여 변경 감지를 수행합니다.

이 함수의 짧은 표현은 다음과 같습니다.

function checkAndUpdateView(view) {
    // update bound properties and run ngOnChanges/ngOnInit/ngDoCheck for child directives
    updateDirectives
    // check all embedded views that belong current view
    execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
    // update DOM for the current view
    updateRenderer
    // perform checkAndUpdate for all child components
    execComponentViewsAction(view, ViewAction.CheckAndUpdate);
}

execEmbeddedViewsActionexecComponentViewsAction 메서드 덕분에 재귀 적으로 실행됩니다. 또한 여기서 핵심은 지시문을 확인한 후 DOM을 업데이트하기 전에 포함 된 뷰를 각도 확인하는 것입니다.

@saverett 예제를 단순화하기 위해 다음 템플릿을 사용합니다.

<div *ngIf="!sharedService.displayList">Some text</div>
<router-outlet></router-outlet>

https://plnkr.co/edit/OdkzHjEXbEd3efYAisFM?p=preview

우리는 이미 *ngIf 가 단지 desugar라는 것을 알고 있습니다. 그래서

<ng-template [ngIf]="sharedService.displayList">
  <div>Some text</div>
</ng-template>
<router-outlet></router-outlet>

다음 구조를 상상할 수 있습니다.

     |
   NgIf directive
       NgIf EmbeddedView
   RouterOutlet directive

RouterOutlet 지시어는 무엇을합니까?

라우팅 된 구성 요소를 ViewContainerRef에 삽입합니다.

activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver|null) {
    ...
    this.activated = this.location.createComponent(factory, this.location.length, injector);

는 것을 의미한다 router-outlet 생성 EmbeddedView 너무 후 RouterOutlet 우리는 우리의 두 임베디드 전망이 지침이 초기화 AppComponent 보기를.

     |
   NgIf directive ([ngIf]="sharedService.displayList")
       NgIf EmbeddedView
   RouterOutlet directive 
      BComponent EmbeddedView

그림에서 설명해 봅시다

haschanged

이제 우리의 주요 기능으로 돌아 갑시다.

function checkAndUpdateView(view) {
    // update bound properties and run ngOnChanges/ngOnInit/ngDoCheck for child directives
    updateDirectives
    // check all embedded views that belong current view
    execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
    ...
    // update DOM for the current view
    updateRenderer
    ...
}

Angular는 다음 순서로 검사를 수행합니다.

NgIf directive ([ngIf]="sharedService.displayList") updateDirectives(1)
      NgIf EmbeddedView   execEmbeddedViewsAction(2)
RouterOutlet directive 
      BComponent EmbeddedView  execEmbeddedViewsAction(3)

NgIf 지시문을 먼저 확인한 다음 (여기서 sharedService.displayList=false 이 기억 됨) BComponent 내부의 NgOnInit 메서드가 호출됩니다 ( sharedService.displayList 변경 true ) angular가 checkNoChanges 하면 Expression has changed after it was checked 오류가 발생합니다.

그런 다음 *ngIf[hidden] 바꾸겠습니다. 이제 RouterOutlet 에 의해 삽입 된 뷰가 하나만 있고 [hidden]="sharedService.displayList"updateRenderer 내에서 확인됩니다.

     |
   div{Some Text} just a node
   RouterOutlet directive 
      BComponent EmbeddedView

Angular는 다음과 같은 검사를 수행합니다.

function checkAndUpdateView(view) {
    // check all embedded views that belong current view
    execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);   BComponent.ngOnInit (1)
    ...
    // update DOM for the current view
    updateRenderer   update hidden binding (2)
    ...
}

이렇게하면 ngOnInit 값을 호출 한 후 업데이트했기 때문에 dom 속성 바인딩에서 오류가 발생하지 않습니다.

이것이 구조적 지시어 *ngIf 와 dom 속성 바인딩 [hidden] 의 주요 차이점입니다.

@alexzuza의 자세한 개요에 감사드립니다! 매우 도움이되었습니다!

setTimeout () 실제로 나를 위해 일했습니다 :) 감사합니다 @jingglang :)
.subscribe (data => setTimeout (() => this.requesting = 데이터, 0))

@robwormald 는 여기에서 정확합니다. 나는 동일한 문제가 있었고 NGRX와 Observable을 사용했기 때문에 감지하기가 더 어려웠습니다. 내가 가진 문제는 상점 상태를 구독하는 경로 계층 구조의 다른 수준에 구성 요소 컨테이너가 있다는 것입니다. 기능 모듈에서 컨테이너 구성 요소를 동일한 수준으로 이동하자마자 문제가 해결되었습니다. 나는 이것이 일종의 복잡한 대답이라는 것을 알고 있지만 NGRX 컨테이너 / 구성 요소 패턴을 사용하는 경우 올바른 방향으로 조사를 할 수 있기를 바랍니다.

setTimeOut으로 작동합니다. @jingglang에게 감사드립니다.
ngAfterViewInit () {
this.innerHeight = (window.innerHeight);
setTimeout (() => this.printPosition (), 0);
}

@alexzuza , 이것은

@ Ninja-Coding-Git 방금 특정 사례를 설명했습니다. 모든 코드에서 발생할 수 있음을 알고 있습니다.

나를 위해 요소를 변경 한 후 ChangeDetectorRef를 호출하면 문제가 해결되었습니다.

import { Component, ChangeDetectorRef } from '@angular/core'

@Component({
  selector: 'my-app',
  template: `<div>I'm {{message}} </div>`,
})
export class App {
  message: string = 'loading :(';

  constructor(private _cdr: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.message = 'all done loading :)'
    this._cdr.detectChanges();
  }

}

여기 에서 찾은 솔루션

안녕하세요 @thetylerb! 이것에 대해 더 자세히 설명해 주시겠습니까? 우리는 ngrx를 사용하고 있으며 이것을 보상하는 방법을 모릅니다. Observable 위에 promise를 사용하는 것은 꽤 어색하다고 생각합니다.

@ piq9117 당신은 또한이 주석을 볼 수 있습니다 https://github.com/angular/angular/issues/18129#issuecomment -326801192 변경 감지와 ngrx 간의 상호 작용에 대해 만들었습니다. 도움이 될 수 있습니다.

@victornoel 감사합니다! 그것은 우리의 상황을 거의 설명합니다. 우리는 중첩 된 불변 데이터 구조 (immutablejs)를 얻었으며 모든 곳에서 비동기 파이프를 사용하고 있습니다. 내 초기 생각은 매장이 수분이 공급되면 매장이 구성 요소 트리를 변경하고 구성 요소 트리는이 변경 사항을 보상하고 또 다른 변경을 수행 할 것이라는 것입니다 .Detection 😓

우리는 중첩 된 불변 데이터 구조 (immutablejs)를 얻었으며 모든 곳에서 비동기 파이프를 사용하고 있습니다. 내 초기 생각은 매장이 수분을 공급 받으면 매장이 구성 요소 트리를 변경하고 구성 요소 트리가이 변경 사항을 보상하고 또 다른 changeDetection을 수행한다는 것입니다.

내 프로젝트에는 일종의 정규화 된 데이터 구조 (중첩 된 데이터 구조 대신)가 있으므로 그렇지 않을 때까지 이러한 종류의 문제를 완화합니다! :영형

당신은 다음 중 하나를 희망합니다.

  • 한 부분을 변경해도 다른 부분에 대한 업데이트가 트리거되지 않도록 저장 데이터 구조를 변경합니다.
  • 비동기 파이프를 통해 저장소를 수신하는 구성 요소의 자식에서 이벤트를 트리거하지 않도록주의
  • 앞서 언급 한 이벤트를 트리거 할 때 수동으로 변경 감지 트리거
  • 다른 시간 또는 setTimeout에서 해당 이벤트를 트리거합니다.

ngrx를 사용하고 있으며 다음과 같은 상황이 발생했습니다.

app.component.html에서 :
<div *ngIf='(appState | async).show'>test</div>
그리고 app.component.ts에서
this.appState= this.store.select("app");

물론이 상황에서는 오류가 발생하지만 다음과 같은 구성 요소에 부울 플래그를 추가하면 다음과 같습니다.
app.component.html-> <div *ngIf='show'>test</div>
app.component.ts->
this.appState.subscribe((state: AppState) => { this.show= state.show; })

오류는 사라졌습니다. 물론 이유는 이해할 수 있지만 귀하의 의견을 알고 싶습니다. 좋은 해결책입니까? 장점과 단점은 무엇입니까 ??

ngrx를 사용하지 않지만 querySelectorAll let chips = [].map.call(document.querySelectorAll('li.chip'), this.chipVisible); 로 간단한 검사에 문제가 있습니다.

@bogdancarChangeDetectorRef.detectChanges() 솔루션은 오류를 억제하지만 처음에 오류를 일으키는 원인을 수정하여 ChangeDetectorRef를 가져올 필요가없는 것이 좋습니다.

Angular 4.4.4에서 rxjs를 사용할 때 동일한 문제가 발생했습니다. DI 서비스에서 제공하는 Observable 객체를 구독합니다. 내 작업은 다음과 같습니다.

this.eventBroadcastService.getCustomEvent({type: 'loginOpen'}).subscribe(e => { setTimeout(() => { this.isBlur = true; }, 0); });

setTimeout을 사용하여 해결합니다. 이제 작동하지만 까다 롭습니다. 기분이 좋지 않습니다.

ngOnChanges (changes : SimpleChanges | any)가 트릭을 수행합니다.

이 문제는 ngDoCheck () 메서드 내에서 동적 변경 매개 변수 논리를 이동 한 후에 간단하게 수정되었습니다.
ngDoCheck(): void { this.checkValid = this.sourceArr.includes((this.chanedVal).toUpperCase()); }

@ asrinivas61 정말 나쁜 생각이라고 생각합니다. ngDoCheck 는 Angular에 의해 많이 호출되고 있으며 sourceArr 에 많은 데이터가 있으면 성능이 손상 될 수 있습니다.

https://angular.io/guide/lifecycle-hooks#other -angular-lifecycle-hooks 참조

지금은 ngAfterViewInit 와 함께 setTimeout ngAfterViewInit 를 사용하는 것이 좋습니다.

setTimeout(_ => /* your code here */, 1000);

예:

  public ngAfterViewInit(): void {
    // We use setTimeout to avoid the `ExpressionChangedAfterItHasBeenCheckedError`
    // See: https://github.com/angular/angular/issues/6005
    setTimeout(_ => this.isLoadingInProgress = false, 1000);
  }

@dgroh 실제 시간 제한을 추가 할 필요는 없습니다. js가 다음 비동기 작업에서 코드를 실행하기 만하면됩니다. 이는 다른 변경 감지 실행을 의미합니다.

@victornoel 저는 detectChangesChangeDetectorRef detectChanges 를 명시 적으로 호출하지 않는 한 이것이 옵저버 블과 함께 작동 할 것이라고 믿습니다. 이 모든 것이 필요하지 않았 으면합니다.

@dgroh 당신이 나를 오해했을 것입니다.

public ngAfterViewInit(): void {
    // We use setTimeout to avoid the `ExpressionChangedAfterItHasBeenCheckedError`
    // See: https://github.com/angular/angular/issues/6005
    setTimeout(_ => this.isLoadingInProgress = false);
  }

그렇지는 이후 단계를 추가 할 수 있습니다 ngAfterViewInit 유사한 setTimeout ? (예 : ngAfterViewLoad )

@ aks4it , 이것은 작동하는 것처럼 보이지만 어떻게 작동하는지에 대한 자세한 내용을 원합니까? 동적 구성 요소 생성 코드가 동기식입니까?

Angular 문서의 공식 plunker 데모 에는 동일한 오류가 있습니다. 거기에서 고칠 수 있습니까? :) 감사

안녕하세요.

  ngAfterViewInit() {
    //this.loadComponent();
    this.getAds();
  }

작동합니까?

나는 귀하의 요구 사항을 이해하지 못하지만 정말 3 초 간격을 원하십니까? 그렇다면 이것이 작동하고 loadComponent를 직접 호출하려는 경우에도 성공했습니다.

ngAfterViewInit() {
    setTimeout(()=>{this.loadComponent()},0)
  }

@istiti 나는 ChangeDetectorRef 해결 방법으로 그것을 고칠 수 있었고, detectChanges() 뒤에 ... data = data

하지만 공식 문서에서 플러 커가 되었기 때문에 문제의 공식적인 해결책이 궁금했습니다. 시간 초과 또는 ChangeDetectorRef가 아닙니다.

첫 번째 게시물에서 너무 짧아서 미안합니다 .. 각도 문서도 코드에서 동일하기 때문에 문제의 심각성을 지적하고 싶었습니다.

참고로, 여기서 실제로 원인이 무엇인지 확실하지 않지만 main.ts에서 prod 모드를 활성화하면 오류가 사라집니다.
`enableProdMode ()

// if (environment.production) {

// enableProdMode ();

//}`

"@ angular / core": "^ 4.2.4"
"typescript": "~ 2.3.3"

@bakthaa ... 각 CD주기 후에 호출되는 컨트롤 검사 (구성 요소 @Inputs() 값이 동일한 지 여부)는 Angular 앱의 프로덕션 모드가 아닌 개발 모드에서만 호출되기 때문에 동작합니다. 따라서 문제는 프로덕션 모드에서 숨겨져 있지만 여전히 존재하며 프로덕션 모드에서 다른 원치 않거나 잘못된 부작용의 원인이 될 수 있습니다.

저는 Angular 5를 사용하고 있는데, 컴포넌트에 ChangeDetectorRef를 주입하고 강제로 변경 사항을 감지하도록함으로써이 문제를 해결할 수있었습니다.

    constructor(private changeDetector: ChangeDetectorRef) {}

    ngAfterViewChecked(){
      this.changeDetector.detectChanges();
    }

@ Sabri-Bouchlema ... 예, 한 가지 방법입니다 ...하지만 다시 한 번 다시 생각해야합니다 ... 왜 구성 요소 @Inputs 가 한 번의 CD주기 동안 변경되는지 ... 논리가 정확합니다 ... 그리고 95 %의 경우에서 추가 detectChanges() 를 호출하지 않고도 문제를 피하기 위해 코드를 변경할 수 있다는 것은 사실입니다.

각도 사용 5.1.2. ngAfterViewChecked에서 재질 대화 상자를 열 때이 오류가 발생합니다. @ aks4it 가보고했듯이 코드를 setTimeout으로 래핑하여 작동하도록해야했습니다.

버전 4.01에서 동일한 문제가 발생합니다.
ng-version = "4.0.1

각도 4.4.6과 동일

this.changeDetector.detectChanges(); 사용하여 수정

템플릿 변수를 사용하여이 플 런커를 작성했는데 왜 ExpressionChangedAfterItHasBeenCheckedError가 있는지 이해할 수 없습니다. 구성 요소의 순서를 변경하면 오류가 사라집니다.

https://plnkr.co/edit/Mc0UkTR2loI7wgmLAWtX

changeDetector.detectChanges를 시도했지만 작동하지 않습니다.

@AlexCenteno ... 평가 로직은 위에서 아래로 ... 그리고 때문에 value 재산 comp1 설정 @Input() public value:String=null; 제 다음 다시 설정의 기준을 사용하여 comp2.value ... <div comp1 [value]="comp2.value"></div> 한편에 설정되어있는 Hello 자체를 통해 @Input() ... ... 모든 withing에 하나의 CD주기 반복 검사 때문에 하위 구성 요소의 입력이 개발 모드의 CD주기 시작시와 동일하면 comp1 value 속성 변경이 감지됩니다.

안녕하세요 @ mlc-mlapis 시간 내 주셔서 감사합니다. 그래서 오류를 피할 방법이 없습니까? enableProdMode를 호출하면 걱정할 필요가없는 것 같습니다.

@AlexCenteno ... 예, prod 모드에서는 CD주기 후 검사가 호출되지 않습니다.하지만 이것이 만족해야한다는 의미는 아닙니다. value 교차 하위 구성 요소를 전달하는 다른 논리를 적용해야합니다. <div> value 속성이 없다는 문제도 있습니다. 속성 구성 요소를 사용하는 이유는 무엇입니까? 왜 안 되는가 :

<comp1 [value]="comp2.value"></comp1>
<comp2 #comp2 value="Hello"></comp2>

@Output() 에서 <comp2> 를 사용하는 것은 괜찮을 것이고 [value] 에서 comp1 를 오류없이 업데이트 할 수 있기 때문입니다.

[email protected]angular@5 standart store.select [email protected] 와 함께 사용하면 개발 모드에서 ExpressionChangedAfterItHasBeenCheckedError 경고가 발생하고 prod 모드에서 잘못 작동합니다. 파견 및 선택은 다른 수준 / 컨테이너에 있습니다.

@Component({
  ...
  template `
  ...
      <div [ngClass]="{
        'home__sidenav': true,
        'home__sidenav--active': (isActiveSidenavMenu$ | async)
      }">
  ...
  `
})
export class HomeContainer {
  isActiveSidenavMenu$ = this.store.select(fromHome.isActiveSidenavMenu);
  ...
}

이 접근 방식은 경고 메시지를 제거하지만 더 복잡해 보입니다.

@Component({
  ...
  template `
  ...
      <div [ngClass]="{
        'home__sidenav': true,
        'home__sidenav--active': isActiveSidenavMenu
      }">
  ...
  `
})
export class HomeContainer {
  isActiveSidenavMenu$ = this.store.select(fromHome.isActiveSidenavMenu);
  isActiveSidenavMenu;

  ngOnInit() {
    this.isActiveSidenavMenu$.subscribe(res => {
      this.isActiveSidenavMenu = res;
      this.cd.detectChanges();
    });
  }
}

이 문제를 더 정확하게 해결하는 방법이 있습니까?

@rimlin 스토어에 직접 구독 해 보셨나요?

ngOnInit() {
  this.store.select(fromHome.isActiveSidenavMenu)
    .subsbrice(res => {
      this.isActiveSidenavMenu = res;
    });
}

비슷한 문제가 있다고 생각합니다. 템플릿에서 async 를 피함으로써 문제가 해결되었습니다.

@ piq9117 조언에 감사하고 솔루션을 시도했습니다. 코드가 적을수록 더 명확 해 보이지만 여기에서도 this.cd.detectChanges(); 를 호출해야합니다. 그렇지 않으면보기가 업데이트되지 않습니다.

ngOnInit() {
    this.store.select(fromHome.isActiveSidenavMenu).subscribe(res => {
      this.isActiveSidenavMenu = res;
      this.cd.detectChanges();
    });
}

Angular 5.2를 사용하고 있습니다. 나를 위해 작동

constructor(private changeDetector: ChangeDetectorRef) {}

ngAfterViewInit(){
  this.changeDetector.detectChanges();
}

Angular 5.2 사용, detectChanges () 사용 수정

예, 우리는 detectChanges () 경로 (Angular 5.1)도 사용하여이 문제를 해결했습니다. 나는 이것이 여전히 해결 방법이며 장기적으로 대답으로 취급되어서는 안된다고 생각하지만 여기에 많은 사람들과 함께 있습니다. 사용할 수있는 다른 수명주기 후크가 있어야합니다.

이와 같은 변경을 허용하기 위해 완전히 새로운 변경 감지주기를 트리거하는 것은 차선책으로 보입니다.

모든 답변과 조언이 이미 위에 게시되었지만 이것은 계속 계속되는 것 같습니다.

  1. 이 문제를 해결하는 올바른 방법은 위에서 아래로 단일 변경 감지 단계로 작동하도록 구성 요소 및 데이터 처리를 재구성 / 재 구조화하는 것입니다. 이는 하위 구성 요소가 상위 구성 요소의 템플릿에서 사용되는 값을 변경할 수 없음을 의미합니다. 구축 한 항목을 다시 평가하고 데이터가 구성 요소 트리 아래로 한 방향으로 만 흐르는 지 확인합니다. 이것은 경우에 따라 ngAfterViewInit 에서 ngOnInit 로 코드를 이동하는 것처럼 간단 할 수 있습니다.
  2. this.changeDetectorRef.detectChanges(); 는 첫 번째 단계가 문제가되는 경우의 5 % 미만에서이를 처리하는 공식적이고 인정 된 방법입니다.

기타 참고 사항 :

  1. DOM에서 사용되는 값을 변경하면이 오류가 발생할 수 있으므로 ngAfterViewInit 에서 작업을 수행 할 때는 매우주의해야합니다.
  2. Check 또는 Checked 라이프 사이클 후크는 모든 변경 감지주기에서 호출 될 수 있으므로 사용시 매우주의하십시오.
  3. setTimeout 는이 문제를 해결할 수 있지만 위의 두 가지 솔루션이 선호됩니다.
  4. 개발 모드에서이 오류를 무시하고 프로덕션 모드에서 "수정"하면 위에서 설명한대로 오류가 유효하지 않습니다. 이러한 검사는 prod 모드에서는 수행되지 않지만 prod 모드 코드는 여전히 변경 사항을 감지하지 않고 그대로두고보기와 데이터가 동기화되지 않습니다. 이는 경우에 따라 무해 할 수 있지만 무시할 경우 데이터 / 뷰 손상 및 더 나쁜 문제로 이어질 수 있습니다.
  5. 한 가지 방법은 데이터 흐름이 Angular에서 실제로 많이 변경되지 않았으므로 Angular 버전 X에서 이것을 게시하는 것은 그리 도움이되지 않습니다.
  6. 더 많은 수명주기 후크 또는 상태를 추가하는 것은 이에 대한 해결책이 아닙니다. 개발자는 데이터가 이러한 후크로 유입되는 단방향을 위반하는 코드를 추가하고 동일한 오류를 트리거합니다.

마지막으로,이 오류는 Angular에 문제가 있음을 나타내는 것이 아니라는 점을 기억하십시오. 이 오류는 코드에서 뭔가 잘못했고 Angular를 빠르고 효율적으로 만드는 단방향 데이터 흐름 시스템을 위반하고 있음을 알리기위한 것입니다.

위의 게시물과 많은 블로그 게시물은 이에 대해 더 자세히 설명하고 있으며이 단방향 데이터 흐름 모델과 구성 요소를 빌드하고 그 안에서 데이터를 전달하는 방법을 이해해야하므로 읽을 가치가 있습니다.

@Splaktar 위의 주석을

@Splaktar 당신은 "이것은 어떤 자식 구성 요소도 부모 구성 요소의 템플릿에서 사용되는 값을 변경할 수 없음을 의미합니다"라고 말합니다.

탐색, 페이지 제목 등을 포함하는 컨테이너 구성 요소가 있고 각각 자체 하위 구성 요소를로드 할 수있는 하위 구성 요소 모음을로드한다고 가정합니다. 컨테이너에로드 된 구성 요소는 데이터에 따라 변경되고로드하는 하위 구성 요소는 데이터에 따라 변경됩니다. 이러한 각 하위 구성 요소는 부모 컨테이너가 자신이 무엇인지에 대한 정보를 부모 컨테이너와 다시 통신 할 수 있어야합니다. 그래야 부모 컨테이너가 적절한 최상위 수준 Nav를 사용자에게 표시 할 수 있습니다 (아마도 탐색 경로 추적 또는 유사한 항목). 보기에로드 된 항목을 컨테이너 수준에서 식별하기 위해 제목에 사용할 수있는 정보입니다.

내 경험상 매우 일반적인 아키텍처 인 여기에서는 하위 구성 요소가 스스로 등록하고 부모 구성 요소가 구독하는 공통 상태 서비스를 사용하여 자녀가 부모와 부모를 교육 할 수 있도록합니다. 적절한 정보를 표시 할 수 있습니다.

이는 상향식 접근 방식을 따르지 않습니다. 부모가 서비스에서 모든 관련 데이터를 가져오고 단방향 통신을 통해 자식 구성 요소에 필요한 모든 정보를 푸시하기 때문입니다.

이것은 위에서 제안한 것과 반대되는 것으로 보이며 저에게 문제의 원인입니다. 하위 구성 요소가 서비스에 등록되면 상위 구성 요소에서 오류가 발생합니다.

이것이 잘못 되었습니까?

승인 된 버그가 있다는 사실을 잊지 마십시오.
https://github.com/angular/angular/issues/15634

@ Martin-Wegner https://github.com/angular/angular/pull/18352#issuecomment -354170352를 참조

@alignsoft ... 귀하의 사례와 문제를 Stackblitz로 간단하게 재현 했습니까? 설명하신 논리 외에 다른 것이 케이스에 영향을 미치는 것으로 보이기 때문입니다.

@Splaktar의 솔루션은 여기 내 간단한 문제와 관련이없는 @trotyl https://github.com/angular/angular/issues/17572#issuecomment -311589290 나?

@ Martin-Wegner ... 귀하의 경우 ngAfterViewInitngOnInit 후크로 변경하는 것으로 충분합니다.

@ MLC-mlapis 말했듯이 @alignsoft하는 Stackblitz의 예는이 사건에 대한 적절한 구조를 보여주는 매우 도움이 될 것입니다. 이는 올바른 서비스, 관찰 가능 항목, 수명주기 후크 등을 사용하여 매우 가능합니다.이 특정 사례는 StackOverflow에 대한 좋은 주제가 될 것입니다.

@Splaktar 현재 마감일이 책상에서 벗어나면 간단한 사례를 보여주는 프로젝트를

@ mlc-mlapis 와우 감사합니다, 작동합니다 :)

@alignsoft , 오늘 저는 여러분이 설명한 것과 똑같이 작동하는 앱을 만들고있었습니다. @ MLC-mlapis, 당신은에 예를 벗었 찾을 수 있습니다 stackblitz . 왼쪽 탐색 메뉴에서 "CMS 시스템"을 클릭하면 ExpressionChangedAfterItHasBeenCheckedError 예외가 발생합니다.

예제에서 admin.component는 다음 세 가지를 담당하는 상위 구성 요소로 작동합니다.

  • 왼쪽 탐색 표시
  • 하위 구성 요소가 자체 시세 메시지를 표시하는 데 사용할 수있는 최고 시세
  • <router-outlet></router-outlet> 통해 하위 구성 요소 별 데이터를 표시하는 기본 콘텐츠 섹션입니다.

부모 구성 요소

<div class="ticker-container" *ngIf="tickedMessage !== ''">
  <h1>{{tickedMessage}}</h1>
  <button type="button" (click)="tickedMessage = ''">
    close
  </button>
</div>

<div class="side-nav">
  <h2>side nav</h2>
  <ul>
    <li><a routerLink="/">dashboard</a></li>
    <li><a routerLink="/apps/thirdpartycms">CMS System</a></li>
  </ul>
</div>
<div class="main-content-container">
  <router-outlet></router-outlet>
</div>
export class AdminComponent implements OnInit, OnDestroy {
  private _tickedMessageSubscription: Subscription;

  tickedMessage: string = '';

  constructor(
    private router: Router,
    private adminService: AdminService
  ) { }

  ngOnInit() {
    /* 
      On the side nav if you click on CMS System, this piece of code throws the exception: 
      "ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed 
       after it was checked. Previous value: 'false'. Current value: 'true'.""
    */
    this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe((tickedMessage: string) => {
      this.tickedMessage = tickedMessage;
    });
  }

  ngOnDestroy() {
    this._tickedMessageSubscription.unsubscribe();
  }
}

<router-outlet></router-outlet> 사용할 때 부모 구성 요소에 데이터 백업을 전달하는 방법을 연구 할 때 @Output 을 사용하여 부모 구성 요소로 데이터를 다시 보내면 라우터 콘센트 지시문이 종료된다는 것을 읽었습니다. 이벤트 처리. 사용자 지정 이벤트이므로 router-outlet 지시문은 처리 방법을 알지 못합니다. 그래서이 방법을 건너 뛰었습니다. angular.io에서 공유 서비스에 대해 읽은 후 직접 작성했습니다.

공유 서비스

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs/Subject';

@Injectable()
export class AdminService {

  private _tickedMessageAnnounced = new Subject<string>();

  tickedMessageAnnounced$ = this._tickedMessageAnnounced.asObservable();

  announceTickedMessage(tickedMessage: string) {
    this._tickedMessageAnnounced.next(tickedMessage);
  }
}

this.adminService.announceTickedMessage(this._tickedMessage); 라는 하위 구성 요소 중 하나 :

하위 구성 요소

import { Observable } from 'rxjs/Observable';
import { Component, ViewChild, OnInit, Inject } from '@angular/core';
import { AdminService } from '../../../core/admin/admin.service';

@Component({
  selector: 'cms',
  templateUrl: './thirdparty-cms.component.html',
  styleUrls: ['./thirdparty-cms.component.scss'],
})
export class ThirdPartyCmsComponent implements OnInit {
  private _tickedMessage: string;
  constructor(private adminService: AdminService) { }

  ngOnInit(): void {
    this._tickedMessage = 'Please do not create an entry for a page URL that does not exist in the CMS system';

    this.adminService.announceTickedMessage(this._tickedMessage);
  }
}

부모 구성 요소가 자식 구성 요소에서 보낸 틱 메시지 값으로 공용 속성 중 하나를 업데이트하려고 시도했기 때문에 ExpressionChangedAfterItHasBeenCheckedError 예외가 발생했습니다.

부모 구성 요소 (예외 발생)

export class AdminComponent implements OnInit, OnDestroy {
  private _tickedMessageSubscription: Subscription;

  tickedMessage: string = '';

  constructor(
    private router: Router,
    private adminService: AdminService
  ) { }

  ngOnInit() {
    /* 
      On the side nav if you click on CMS System, this piece of code throws the exception: 
      "ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed 
       after it was checked. Previous value: 'false'. Current value: 'true'.""
    */
    this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe((tickedMessage: string) => {
      this.tickedMessage = tickedMessage;
    });
  }

  ngOnDestroy() {
    this._tickedMessageSubscription.unsubscribe();
  }
}

ExpressionChangedAfterItHasBeenCheckedError 를 해결 한 것은 구독 콜백을 비동기 적으로 실행하고 틱 메시지를 기다리는 것입니다.

부모 구성 요소 (비동기 대기 포함)

export class AdminComponent implements OnInit, OnDestroy {
  private _tickedMessageSubscription: Subscription;

  tickedMessage: string = '';

  constructor(
    private router: Router,
    private adminService: AdminService
  ) { }

  ngOnInit() {
    /* 
      This clears the ExpressionChangedAfterItHasBeenCheckedError exception.
    */
    this._tickedMessageSubscription = this.adminService.tickedMessageAnnounced$.subscribe(async (tickedMessage: string) => {
      this.tickedMessage = await tickedMessage;
    });
  }

  ngOnDestroy() {
    this._tickedMessageSubscription.unsubscribe();
  }
}

WPF 세계에서 왔을 때 Observable이 비동기 적으로 실행되기 때문에 Observable 구독 콜백을 기다리도록 UI 스레드에 지시해야 할 것 같습니다.

저는 Angular로 시작하는 중이므로 Angular 기술에 대한 연민을 부탁드립니다.

@ktabarez 감사합니다, 이것은 저를 많이 도왔습니다!

@ktabarez 감사합니다! 잘 작동하는 것 같습니다! 부모 구성 요소의 구독을 제한 시간 내에 래핑하고 있었는데이 역시 작동했지만 더러워졌습니다.

나는 redux 상태를 조사 할 때 보장되는 것이 보장되지만 생성자가 아닌 ngOnInit에 넣을 때와 같은 시작시

@ktabarez 정말 감사합니다!

async ... await in subscribe 솔루션이 작동합니다.

@ktabarez 솔루션은 기본적으로 setTimeout ({}, 0)과 동일하지만 더 우아하게 느껴집니다. 아주 좋아! 그것은 또한 무엇이든 기다릴 수 있다는 것을 깨달았습니다 (C # await Task.FromResult (tickedMessage)와 거의 비슷 함).

그래서 질문 : 자주 변경 될 수있는 백엔드에서 데이터를 가져 오는 옵저버 블에 대한 속성 계산에 대해 비동기 / 대기해야하는 이유는 무엇입니까? ngClass가 이것이 필요한 이유는 무엇입니까? 나는 {observable | async} 파이프. 그래서 async / await는 나에게 가장 좋은 방법이 아닙니다. (또는 비동기 파이프가 최선의 방법이 아닙니다)

부모 구성 요소에 여러 자식 구성 요소가있는이 문제가 있습니다. 자식 구성 요소 중 하나에는 부모 구성 요소 속성의 데이터가 필요한 Input 이 있고 속성은 Observable / Promise (둘 다 시도)에서 데이터를 가져옵니다.

parent.component.html

<mat-tab-group>
   <mat-tab>
      <app-child-1></app-child-1>
   </mat-tab>
   <mat-tab>
      <app-child-2></app-child-2>
   </mat-tab>
   <mat-tab>
      <app-child-3 [datasource]="data"></app-child-3>
   </mat-tab>
</mat-tab-group>

parent.component.ts

// ... omitted...
data: any;

ngOnInit() {
   this.service1.getMethod(some_id).then(data => this.data = data);
}

스레드에서 찾은 모든 솔루션을 발견했습니다. 그러나 내 경우에는 아무것도 작동하지 않습니다. 다행스럽게도 일부는 _ (ngIf) _에 표현식을 예제로 넣는 것과 같이 DOM 조작으로 인한 문제라고 암시했습니다.

그래서 이것을 피하기 위해 ng-template , ng-containerngTemplateOutlet을 실험 해

<ng-container *ngTemplateOutlet="data ? content : loading"></ng-container>

<ng-template #content>
   <mat-tab-group>
      <mat-tab>
         <app-child-1></app-child-1>
      </mat-tab>
      <mat-tab>
         <app-child-2></app-child-2>
      </mat-tab>
      <mat-tab>
         <app-child-3 [datasource]="data"></app-child-3>
      </mat-tab>
   </mat-tab-group>
</ng-template>

<ng-template #loading>Loading your component. Please wait</ng-template>

ChangeDetectorRef 또는 setTimeout() 를 사용하지 않고도이 문제를 해결할 수있었습니다.

저에게는 조건에 따라 필드를 보이거나 숨길 때이 오류가 발생했는데, 필드 설정 값을 적용한 후 해결할 수있었습니다.

this.form.get ('field').updateValueAndValidity();

나는 init에서 열리는 mat-dialog로 이것을 시도해 왔으며 문자 그대로이 스레드에서 본 모든 솔루션을 시도했습니다 (set timeout, promise resolve, observable pipe, after view init, after content init) 및 그 안의 조합. 여전히 작동하지 않습니다. 그런 다음 @Splaktar 게시물이 이것이 단방향 데이터 흐름을 어떻게 위반하는지에 대한 게시물을 보았

init에서 :
this.store.pipe( select(getShouldLogin), ).subscribe((shouldLogin: boolean) => { if (shouldLogin) { this.openLoginDialog(); } }); this.checkUserId();

보조 기능 :
checkUserId() { const id = this.cookies.getUserId(); if (!id || id === 'undefined') { this.connApi.setShouldLogin(true); } }
openLoginDialog() { const dialogRef = this.dialog.open(LoginDialogComponent, { width: '400px', height: '300px', }); dialogRef.afterClosed().subscribe(result => this.handleLogin(result)); }

각도 6.0.0-rc.1 사용

ngrx와 함께 angular를 사용하고 있으며 양식을 제출 한 후이 오류가 발생했습니다.
설정으로 고정
ChangeDetectionStrategy.OnPush
양식 구성 요소

여전히 문제가 있습니다. 모든 종류의 해결 방법을 시도했습니다. setTimeout 은 처음에는 작동하지만 이후에 dialog.open 트리거하면 동일한 오류가 발생합니다. 이것에 대한 어떤 상태?

나는 같은 문제에 직면하고 있습니다. 모든 것을 시도했지만이 문제는 일방적 인 데이터 흐름에서 발생하는 것 같습니다.
여기에서 selectedwoids를 업데이트하려고합니다. 문제가 해결되지만 동일한 오류가 발생합니다.

        <p-row> <p-column class="text-left" footer="{{selectedWoids.length}} {{getWoidString(selectedWoids.length)}} selected out of {{getTotalCounts()}} {{getWoidString(getTotalCounts())}}" colspan="11"></p-column> </p-row>

private setSelectedWoidsCount () {
if (this.isSelectAllWoids) {
this.selectedWoids = this.allWoids;
}
}

Angular의 렌더링 파이프 라인을 깨기 때문에 ...

4.1.2 에서 4.2.3 전환 한 후 유사한 "ExpressionChanged ..."오류가 표시됩니다.

제 경우에는 서비스를 통해 상호 작용하는 두 가지 구성 요소가 있습니다. 서비스는 내 메인 모듈을 통해 제공되며 ComponentB 전에 ComponentA 이 생성됩니다.

4.1.2 에서 다음 구문은 오류없이 잘 작동했습니다.

_ComponentA 템플릿 : _

<ul *ngIf="myService.showList">
...

_ComponentB 클래스 : _

ngOnInit() {
  this.myService.showList = false; //starts as true in the service
  ...
}

4.2.3 에서 "ExpressionChanged ..."오류를 방지하려면 ComponentA 의 템플릿을 다음과 같이 수정해야합니다.

_ComponentA 템플릿 : _

<ul [hidden]="!myService.showList">
...

나는 이것이 단지 문제가 된 이유와 *ngIf 에서 [hidden] 로의 전환이 작동하는 이유를 파악하려고 여전히 노력하고 있습니다.

ngIf에서 [숨김]으로 전환 할 때 작동 방식을 찾았습니까?

Angular 6에서이 문제에 대한 해결책이 아직 있습니까?

@alokkarma ... 너무 오래되었습니다 ... 최소한 Angular 6 또는 5로 마이그레이션해야합니다. 하지만 예제 코드는 명확 해 보입니다. 하위 구성 요소 B의 서비스 속성을 변경하고 있으며 이는 하위 구성 요소 A의 일부 동작에 영향을줍니다. 두 하위 구성 요소는 모두 동일한 상위 구성 요소에 배치되므로 세 가지 모두 하나의 CD 사이클에서 관계. 당신은 무엇을 기대합니까?

동일한 케이스가 버전 4.1.2에서 작동했다는 사실은 4.2.3을 포함한 이후 버전에서 제거 된 일부 버그와 관련이 있습니다.

Angular 6을 사용하고 있습니다. 사용자가 로그인 한 경우 (또는 로그인하지 않은 경우) 다른 탐색 모음 콘텐츠를 표시하기 위해 인증 구성 요소와 앱 구성 요소간에 통신하기 위해 MessageService를 사용하고 있습니다.

이 오류를 제외하고는 잘 작동합니다!
EventEmitter와 같은 다른 솔루션을 시도했지만 작동하지 않습니다! 또는 동일한 오류가 발생합니다.

@dotNetAthlete ...

@dotNetAthlete 기본 컨테이너 구성 요소에서 페이지 제목이 동작 주제 (asObservable)로 포함 된 상태 서비스와 상태 서비스에서 적절한 페이지 제목을 설정하는 하위 구성 요소가로드 된 상태 서비스가있는 동일한 기본 메커니즘을 사용하고 있습니다. 부모 컨테이너가 관찰하고 표시합니다.

컨테이너가 값을 초기화 한 다음 자식 구성 요소가 즉시이를 업데이트 할 때 항상 '표현식 변경'오류가 있었으므로 이제 부모 / 컨테이너에서이 작업을 수행합니다.

    this.stateSvc.currentPageTitle$
      .subscribe(
        (async (pageTitle) => {
          this.pageTitle = await pageTitle;
        }))

주정부 서비스에는 다음이 있습니다.

  // Store
  private currentPageTitleStore = new BehaviorSubject<string>("");

  // Getter (as Observable)
  public currentPageTitle$ = this.currentPageTitleStore.asObservable();

  // Setter
  public setCurrentPageTitle(pageTitle: string) {
    this.currentPageTitleStore.next(pageTitle);
  }

이것은 문제를 제거합니다. 이 솔루션은이 스레드에있는 다른 사람들의 유용한 피드백을 기반으로합니다.

@alignsoft- 아름다운 솔루션

나는 지금 각도 7에서 페이지 다시로드에서 이것을보고 있습니다 ...
부모 ngAfterViewInit 에 자식 구성 요소 값을 설정하고 있습니다.
하위 구성 요소에서이 작업을 수행해야했습니다.

  public activate() {
    setTimeout(() => this.active = true);
  }

이 문제 스레드 https://github.com/angular/angular/issues/10762 에서 해결책을 찾았습니다.
AfterContentInit 라이프 사이클 후크를 사용한 후 오류가 사라집니다.

@alignsoft , 우리는 Subject 대신 BehavioralSubject를 사용하고 있기 때문에 변경 사항에 추가합니다.

다음과 같이 수정했습니다.

\\ Component
export class AppComponent implements OnInit {
  isLoading: Boolean;

  constructor(private loaderService: LoaderService) { }

  ngOnInit(){
    this.loaderService.isLoading.subscribe(async data => {
      this.isLoading = await data;
    });
  }
}
\\ Service
@Injectable({
  providedIn: 'root'
})
export class LoaderService {
  // A BehaviorSubject is an Observable with a default value
  public isLoading: BehaviorSubject<Boolean> = new BehaviorSubject(false);
  constructor() {}
}
\\ Any other component or in my case, an interceptor that has the LoaderService injected
this.loaderService.isLoading.next(true);

그러나 나는 @daddyschmack에 동의합니다. 위에서 언급 한 수정 사항 ( cdr.detectionChanges() 또는 async await 또는 aftercontentchecked ) 중 하나를 사용하지 않고 관련 정보를 가리키는 리소스를 얻고 싶습니다. 이유 [hidden] 속성은 작동하지만 *ngIf 는 작동하지 않습니다.

@acidghost 해당 링크에 감사드립니다. 내 문제를 해결했습니다. 나는 실제로 내 응용 프로그램의 한 영역에서 수정하려는 시도를 포기했지만 새로운 영역에서 다시 발생하기 시작했을 때 ...

이 문제는 활동이 없어 자동으로 잠겼습니다.
유사하거나 관련된 문제가 발생하면 새로운 문제를 제출하십시오.

자동 대화 잠금 정책 에 대해 자세히 알아보십시오.

_이 작업은 봇에 의해 자동으로 수행되었습니다 ._

이 페이지가 도움이 되었나요?
0 / 5 - 0 등급