首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >可爱的ExpressionChangedAfterItHasBeenCheckedError

可爱的ExpressionChangedAfterItHasBeenCheckedError
EN

Stack Overflow用户
提问于 2020-09-22 12:06:50
回答 1查看 152关注 0票数 0

我有

  • 输入输入学生电子邮件或IAM >它将被添加到一个string[]
  • 列出我添加的所有学生的列表>和for循环,如下所示
  • 按钮从数组中删除学生

列表中有一个“maxHeight”,我需要检查内容何时高于“滚动高度”,显示一个按钮来读取更多内容,否则就隐藏它。

我让它工作了,但我有一个问题,当我增加这么多的学生,高度超过‘many’,他显示的按钮,这是完美的,但是,当我删除更多的学生,直到它应该再次隐藏按钮,然后错误将被触发。

SetTimeout不是我的解决方案,只是没有其他的解决方案。

代码语言:javascript
运行
复制
<div class="input-row">
  <input #studentsInput
         (keypress)="onKeyPressStudent($event, studentsInput)"
         class="input-field"
         placeholder="Student Email or IAM"
         required
         type="text">
</div>

<ul #listStudents
    [ngClass]="{ 'scrollable': studentsScroll }"
    class="list-row">
  <li *ngFor="let student of students; index as i"
      class="list-item">
    {{ student }}

    <span (click)="deleteStudent(i)"
          class="material-icons">
      close
    </span>
  </li>
</ul>

<div *ngIf="listStudents.offsetHeight < listStudents.scrollHeight"
     class="more">
  <span class="material-icons">
    more_horiz
  </span>
</div>
代码语言:javascript
运行
复制
export class Component implements OnInit {
  public students: string[] = [];
  public studentsScroll = false;

  public ngOnInit(): void {
  }

  public onKeyPressStudent(
    event: KeyboardEvent,
    studentsInput: HTMLInputElement,
  ): void {
    if (event.key === 'Enter') {
      if (studentsInput.checkValidity()) {
        this.students.push(studentsInput.value);
        studentsInput.value = '';
      } else {
        this.toastrService.error('Please enter a valid Email Address!');
      }
    }
  }

  public deleteStudent(index: number): void {
    this.students.splice(index, 1);
  }

  public toggleStudentScroll(): void {
    this.studentsScroll = !this.studentsScroll;
  }
}

控制台错误

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-22 16:41:49

当您使用deleteStudent()时,学生数组会发生变异(剪接),列表项(li)的数量也会由于所使用的*ngFor指令而改变。这将导致绑定到*ngIf指令的listStudents元素属性更改(offsetHeight,scrollHeight)。

因此,表达式"listStudents.offsetHeight < listStudents.scrollHeight“在更改检测(由学生数组突变触发)通过模板后发生变化。

要解决这个问题,您需要确保检查(显示/隐藏按钮)发生在初始CD之外(这就是为什么setTimeout在创建自己的CD时工作)。

您可以通过使用afterContentChecked lifeCycle钩子:https://angular.io/api/core/AfterContentChecked来实现这一点。

并通过评估该钩子中的语句来评估"showmore“是否应该在DOM (true)中(false):

代码语言:javascript
运行
复制
<div class="input-row">
  <input #studentsInput
         (keypress)="onKeyPressStudent($event, studentsInput)"
         class="input-field"
         placeholder="Student Email or IAM"
         required
         type="text">
</div>

<ul #listStudents
    [ngClass]="{ 'scrollable': studentsScroll }"
    class="list-row">
  <li *ngFor="let student of students; index as i"
      class="list-item">
    {{ student }}

    <span (click)="deleteStudent(i)"
          class="material-icons">
      close
    </span>
  </li>
</ul>

<div *ngIf="showmore" class="more">
  <span class="material-icons">
    more_horiz
  </span>
</div>

在ts中:

代码语言:javascript
运行
复制
import { Component, ViewChild, ElementRef } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  @ViewChild('listStudents', { static: true }) listStudents: ElementRef;
  private listStudentsNativeElement: HTMLElement;

  public showmore: boolean = false;

  public students: string[] = [];
  public studentsScroll = false;

  public ngOnInit(): void {
  }

  public onKeyPressStudent(
    event: KeyboardEvent,
    studentsInput: HTMLInputElement,
  ): void {
    if (event.key === 'Enter') {
      if (studentsInput.checkValidity()) {
        this.students.push(studentsInput.value);
        studentsInput.value = '';
      } else {
        console.log('Please enter a valid Email Address!');
      }
    }
  }

  public deleteStudent(index: number): void {
    this.students.splice(index, 1);
  }

  public toggleStudentScroll(): void {
    this.studentsScroll = !this.studentsScroll;
  }

  ngAfterViewInit() {
    this.listStudentsNativeElement = this.listStudents.nativeElement;
  }

  ngAfterContentChecked() {
    if (this.listStudentsNativeElement) {
      this.showmore = (this.listStudentsNativeElement.offsetHeight < this.listStudentsNativeElement.scrollHeight);
    }
  }
}

很少有人注意到:

  • 我们需要在完整init之后获得listStudents nativeElement引用(ngAfterViewInit)
  • 在视图初始化之前,我们仍然必须检查listStudents nativeElement是否被定义为ngAfterContentChecked激发。

相反,我使用setTimeout或RequestAnimationFrame API:

代码语言:javascript
运行
复制
import { Component, ViewChild, ElementRef } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  @ViewChild('listStudents', { static: true }) listStudents: ElementRef;
  private listStudentsNativeElement: HTMLElement;

  public showmore: boolean = false;
  public students: string[] = [];
  public studentsScroll = false;

  public ngOnInit(): void {
  }

  public onKeyPressStudent(
    event: KeyboardEvent,
    studentsInput: HTMLInputElement,
  ): void {
    if (event.key === 'Enter') {
      if (studentsInput.checkValidity()) {
        this.students.push(studentsInput.value);
        studentsInput.value = '';
        this.checkForShowMore();
      } else {
        console.log('Please enter a valid Email Address!');
      }
    }
  }

  public deleteStudent(index: number): void {
    this.students.splice(index, 1);
    this.checkForShowMore();
  }

  public toggleStudentScroll(): void {
    this.studentsScroll = !this.studentsScroll;
  }

  checkForShowMore() {
    requestAnimationFrame(()=>{
      this.showmore = (this.listStudentsNativeElement.offsetHeight < this.listStudentsNativeElement.scrollHeight);
    })
  }

  ngAfterViewInit() {
    this.listStudentsNativeElement = this.listStudents.nativeElement;
  }

}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64009412

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档