programing

Expression Changed After It Has Be Checked Error: 식이 확인된 후 변경되었습니다.이전 값: '정의되지 않음'

showcode 2023. 6. 9. 22:10
반응형

Expression Changed After It Has Be Checked Error: 식이 확인된 후 변경되었습니다.이전 값: '정의되지 않음'

스택 오버플로에 이미 많은 동일한 질문이 게시되어 있고 런타임 오류를 방지하기 위해 다른 솔루션을 사용해 봤지만 어느 것도 작동하지 않습니다.

오류

구성 요소 및 HTML 코드

export class TestComponent implements OnInit, AfterContentChecked {
    @Input() DataContext: any;
    @Input() Position: any;
    sampleViewModel: ISampleViewModel = { DataContext: : null, Position: null };
    constructor(private validationService: IValidationService, private modalService: NgbModal, private cdRef: ChangeDetectorRef) {
    }

    ngOnInit() {

    }
    ngAfterContentChecked() {

            debugger;
            this.sampleViewModel.DataContext = this.DataContext;
            this.sampleViewModel.Position = this.Position;

    }


<div class="container-fluid sample-wrapper text-center" [ngClass]="sampleViewModel.DataContext?.Style?.CustomCssClass +' samplewidget-'+ sampleViewModel.Position?.Columns + 'x' + sampleViewModel.Position?.Rows">
     //some other html here
</div>

참고: 이 구성 요소는 동적 구성 요소 로더를 사용하여 동적으로 로드됩니다.

문제를 해결한 후 몇 가지 문제를 확인했습니다.

먼저 이 하위 구성 요소는 Dynamic Component Resolver를 사용하여 아래와 같은 입력 값을 전달함으로써 동적으로 로드됩니다.

 ngAfterViewInit() {
    this.renderWidgetInsideWidgetContainer();

  }


  renderWidgetInsideWidgetContainer() {
    let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
    let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this.widgetHost.viewContainerRef;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);
    debugger;
    (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext;
    (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position;

  }

하위 구성요소 html을 아래와 같이 변경해도 동일한 오류가 발생합니다.Angular ngclass 속성만 추가하면 됩니다.

<div class="container-fluid ds-iconwidget-wrapper text-center" [ngClass]="Sample">

</div>

제 데이터 바인딩과 모든 것이 잘 작동합니다.상위 구성 요소에 대해 수행해야 하는 작업이 있습니까?하위 구성 요소의 모든 라이프사이클 이벤트를 이미 시도했습니다.

당신은 말해야 합니다.angular다음 시간 이후에 콘텐츠를 업데이트한 경우ngAfterContentChecked 가져올 수 .ChangeDetectorRef@angular/core 와를 요.detectChanges

import { ChangeDetectorRef } from '@angular/core';

constructor( private cdref: ChangeDetectorRef ) {}   

ngAfterContentChecked() {
    this.sampleViewModel.DataContext = this.DataContext;
    this.sampleViewModel.Position = this.Position;
    this.cdref.detectChanges();
 }

ngAfterContentChecked수명 주기 후크는 하위 구성 요소/지시에 대한 바인딩 업데이트가 이미 완료된 경우 트리거됩니다.그러나 당신은 당신의 바인딩 입력으로 사용되는 속성을 업데이트하고 있습니다.ngClass문제입니다.그것이 문제입니다.Angular가 유효성 검사 단계를 실행할 때 속성에 대한 보류 중인 업데이트가 있음을 감지하고 오류를 발생시킵니다.

오류를 더 잘 이해하려면 다음 두 가지 기사를 읽으십시오.

당이왜부변을하경생요세는보각해지야에서 에 대해 .ngAfterViewInit라이프 사이클 훅이전에 트리거된 다른 라이프사이클ngAfterViewInit/Checked를 들어 를들어다, 니합작동예와 같이 합니다.ngOnInit,ngDoCheck또는ngAfterContentChecked.

그래서 고치기 위해서, 이동합니다.renderWidgetInsideWidgetContainer에▁ngOnInit()라이프 사이클 훅

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

그것은 이 문제를 해결하기 위한 좋은 해결책입니다.

저는 문제가 있었습니다.

ERROR: Expression Changed After It Has Be Checked Error: 식이 Checked 후에 변경되었습니다.'mat-checkbox-checked'의 이전 값: 'true'입니다.현재 값: 'false'.

여기서 문제는 업데이트된 값이 다음 번 변경된 탐지 주기가 실행될 때까지 탐지되지 않는다는 것입니다.

가장 쉬운 해결책은 변경 탐지 전략을 추가하는 것입니다.코드에 다음 행을 추가합니다.

import { ChangeDetectionStrategy } from "@angular/core";  // import

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "abc",
  templateUrl: "./abc.html",
  styleUrls: ["./abc.css"],
})

사중인경우를 .<ng-content>와 함께*ngIf당신은 반드시 이 루프에 빠질 것입니다.

은 바꾸는 이었습니다.*ngIfdisplay:none

두 가지 솔루션:

  1. 바인딩 변수가 있으면 해당 코드를 set timeout({ }, 0)으로 이동하십시오.
  2. 관련 코드를 ngAfterView로 이동Init 방법

*NgIf할 수 에, 없음 하거나 더 은 사용하는 것입니다.[hidden]="!condition"

setTimeout(() => { /* your code here */ }, 0);

setTimeout으로 코드를 포장했는데 작동했습니다.

저는 당신과 같은 일을 하려고 시도하는 것과 같은 문제를 겪었고 리치 프레드릭슨의 대답과 비슷한 것으로 해결했습니다.

createComponent()를 실행하면 정의되지 않은 입력 변수로 생성됩니다.그런 다음 이러한 입력 변수에 데이터를 할당하면 변경 사항이 변경되고 하위 템플릿에서 오류가 발생합니다(내 경우에는 ngIf의 입력을 사용했기 때문에 입력 데이터를 할당하면 변경됨).

이 특정한 경우에 내가 피할 수 있는 유일한 방법은 당신이 데이터를 할당한 후에 변경 감지를 강제하는 것이지만, 나는 콘텐츠 확인 후()에 수행하지 않았습니다.

예제 코드를 따르기는 어렵지만, 제 솔루션이 당신에게 효과가 있다면 (부모 구성 요소에서) 다음과 같은 것이 될 것입니다.

export class ParentComponent implements AfterViewInit {
  // I'm assuming you have a WidgetDirective.
  @ViewChild(WidgetDirective) widgetHost: WidgetDirective;

  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    private changeDetector: ChangeDetectorRef
  ) {}

  ngAfterViewInit() {
    renderWidgetInsideWidgetContainer();
  }

  renderWidgetInsideWidgetContainer() {
    let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this.widgetHost.viewContainerRef;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);
    debugger;
    // This <IDataBind> type you are using here needs to be changed to be the component
    // type you used for the call to resolveComponentFactory() above (if it isn't already).
    // It tells it that this component instance if of that type and then it knows
    // that WidgetDataContext and WidgetPosition are @Inputs for it.
    (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext;
    (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position;
    this.changeDetector.detectChanges();
  }
}

호스트 요소가 여러 개 있기 때문에 @ViewChild 대신 @ViewChildren을 사용하는 것을 제외하고는 내 것과 거의 같습니다.

RxJS 타이머()

다른 사람들이 제안한 것처럼, 당신은 당신의 코드를 내부에 배치함으로써 문제를 해결할 수 있습니다.setTimeout(..)을 이벤트 스택 Angular의 이동합니다.기본적으로 실행을 이벤트 루프 스택 외부로 이동하여 Angular의 다음 변경 감지 사이클로 이동합니다.

RxJS에는 다음과 같은 자체 타임아웃 구현이 있습니다.timer()밀리초를 인수로 수락하고 관찰 가능한 값을 반환합니다.이벤트 루프 스택이 정리된 후(Angular가 모든 렌더링 계산을 완료한 후)에만 실행이 필요하므로,timer()인수 없이(어쨌든 기본값은 0):

ngAfterViewInit() {
    timer().subscribe(() => {
        // your code here
    })
}

JS 이벤트 루프 스키마:

여기에 이미지 설명 입력

은 PS에서 : 내 오 세 부 정 는 약 니 다 정 가 변 된 경 것 다 는 점 했 에 서 감 지 을 보 세 다 내 부 릅 오mat-focused부터의 가치로에서 .truefalse.

제가 조사하는 동안, 저는 오류가 발생했다는 것을 알아차렸습니다. 왜냐하면 제가 그것을 바인딩하려고 했기 때문입니다.SelectedIndexmat-tab-group한 합니다.mat-select내부의 필드tab 1탭 그룹(아래의 코드 발췌).

HTML 추출:

  <form [formGroup]="form" (ngSubmit)="onSubmit()">
    <mat-tab-group animationDuration="500ms" [selectedIndex]="titleId.value">
      <mat-tab label="Tab 1">
        <mat-radio-group formControlName="titleId">
          <mat-radio-button value="1">Mr</mat-radio-button>
          <mat-radio-button value="2">Mrs</mat-radio-button>
        </mat-radio-group>
      </mat-tab>
      <mat-tab label="tab 2">
        ...
      </mat-tab>
      <mat-tab label="tab 3">
        ...
      </mat-tab>
    </mat-tab-group>
  </form>

내 코드가 예상대로 작동했음에도 불구하고(즉, 드롭다운 목록에서 선택한 항목을 기준으로 탭 전환), 이 오류는 무시하기에는 상당히 답답했습니다.유감스럽게도, 여기서 제안된 해결책은 저에게 효과가 없었습니다.하지만, 나는 이사를 하는 것을 알아차렸습니다.mat-radio-group바깥쪽에mat-tab-group구성 요소, 문제 해결.

새로운 구현:

  <form [formGroup]="form" (ngSubmit)="onSubmit()">
    <mat-radio-group formControlName="titleId">
      <mat-radio-button value="1">Mr</mat-radio-button>
      <mat-radio-button value="2">Mrs</mat-radio-button>
    </mat-radio-group>
    <mat-tab-group animationDuration="500ms" [selectedIndex]="titleId.value">
      <mat-tab label="Tab 1">
        ...
      </mat-tab>
      <mat-tab label="tab 2">
        ...
      </mat-tab>
      <mat-tab label="tab 3">
        ...
      </mat-tab>
    </mat-tab-group>
  </form>

저는 아래 코드가 문제를 해결할 것으로 예상했지만 해결되지 않았습니다.제가 뭔가를 제대로 하지 못했나 봅니다.

abc.component.ts 코드 추출

  ngAfterViewInit() {
    setTimeout(() => {
      this.cdRef.detectChanges(); /*cdRef injected in constructor*/
    }, 0);
  }

저는 여전히 이것을 고칠 더 좋은 방법이 있다고 믿습니다.

코딩을 OnInit()로 호출하려면 이 방법을 사용해 보십시오.

someMethod() // emitted method call from output
{
    // Your code 
}

ngOnInit(){
  someMethod(); // call here your error will be gone
}

언급URL : https://stackoverflow.com/questions/45467881/expressionchangedafterithasbeencheckederror-expression-has-changed-after-it-was

반응형