ExpressionChangedAfterItHasBeenCheckedError-共享服务

时间:2019-03-22 06:21:17

标签: angular

为什么我在控制台中收到以下警告?一切似乎都按预期进行,但是Angular抱怨。这个问题有什么解决方案?

StackBlitz是here

我知道一种可能的解决方案是通过父子通信而不是使用服务来通信事件,但这不是我的选择,因为这是较大代码库中问题的隔离。

错误消息

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: false'. Current value: 'ngIf: true'.
    at viewDebugError (core.js:20439)
    at expressionChangedAfterItHasBeenCheckedError (core.js:20427)
    at checkBindingNoChanges (core.js:20529)
    at checkNoChangesNodeInline (core.js:23400)
    at checkNoChangesNode (core.js:23389)
    at debugCheckNoChangesNode (core.js:23993)
    at debugCheckDirectivesFn (core.js:23921)
    at Object.eval [as updateDirectives] (AppComponent.html:6)
    at Object.debugUpdateDirectives [as updateDirectives] (core.js:23910)
    at checkNoChangesView (core.js:23288)

app.component.html

<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <h1  *ngIf="mainSectionContent?.buttontext?.length > 0">
    Welcome to {{ title }}!
  </h1>
  <app-employee></app-employee>
</div>

AppComponent

export class AppComponent implements OnInit {

  title = 'expression-changed';
  mainSectionContent:MainSectionContent;
  contentAnnounce$:Observable<MainSectionContent>;

  constructor(private mainContentService:MaincontentService) { }

  ngOnInit(): void {
    this.contentAnnounce$ = this.mainContentService.contentAnnounce$;
    this.contentAnnounce$.subscribe(mainSectionContent => 
      {
        this.mainSectionContent = mainSectionContent
      }
      );
  }
}

EmployeeComponent

export class EmployeeComponent implements OnInit {

  constructor(private mainSectionContentService:MaincontentService) { }

  ngOnInit() {
    this.mainSectionContentService.announceContent({
      mainheading:'Employee Manger',
      mainsubheading:'To manage PrivilegeManager Employees',
      sectionheading:'Employee List',
      buttontext:'Create Employee'
    });
  }
}

MaincontentService

@Injectable({
  providedIn: 'root'
})
export class MaincontentService {

  private contentAnnounce = new Subject<MainSectionContent>();
  contentAnnounce$ = this.contentAnnounce.asObservable();

  constructor() { }

  announceContent(content:MainSectionContent){
    this.contentAnnounce.next(content);
}  
}

3 个答案:

答案 0 :(得分:2)

我曾经遇到过同样的问题,但是它以有角度的本机方式工作,您可以按照以下代码使用angular / core类的ChangeDetectorRef:

constructor(private cdf: ChangeDetectorRef){}

在您的情况下,在控制器中收到数据后添加以下行:

this.mainSectionContent = mainSectionContent
this.cdf.detectChanges();

这是在要求角度服务重新检查DOM上的更改。

答案 1 :(得分:1)

情况是已经检查*ngIf="mainSectionContent?.buttontext?.length > 0"之后调用了EmployeeComponent的ngOnInit。

首先检查父组件,然后转到子EmployeeComponent,该子组件更改值,该值已用于在同一事件循环迭代中呈现父组件。

这看起来像是骇客,但您必须在第一个循环通过后调用announceContent。您可以在第一个事件循环结束后尝试调用它:

  ngOnInit() {
    setTimeout(() => {
        this.mainSectionContentService.announceContent({
        mainheading:'Employee Manger',
        mainsubheading:'To manage PrivilegeManager Employees',
        sectionheading:'Employee List',
        buttontext:'Create Employee'
        });
      }, 
    0);
  }

答案 2 :(得分:0)

好吧,这个 ExpressionChangedAfterItHaHasBeenCheckedError:错误已经持续了一段时间,看来唯一,最好的解决方法是使用:

setTimeout(() => {...changes...});

---or---

Promise.resolve(null).then(() => {...changes...});

但是要存档必须在每个组件或区域上实现的存档,您想要进行这些更改。那可能是一个真正的麻烦。

因此,我认为最好的方法是将类display: none绑定到要隐藏的HtmlElement上。


app.component.scss | css

.d-none {
  display: none
}

app.component.html

<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <h1 [class.d-none]="mainSectionContent?.buttontext?.length > 0">
    Welcome to {{ title }}!
  </h1>
  <app-employee></app-employee>
</div>

app.component.ts |任何地方

ngOnInit() {
  this.mainSectionContentService.announceContent({
    mainheading:'Employee Manger',
    mainsubheading:'To manage PrivilegeManager Employees',
    sectionheading:'Employee List',
    buttontext:'Create Employee'
  });
}