【问题标题】:Angular 2: focusing input causes 'expression has changed after it was changed' errorAngular 2:聚焦输入导致“表达式在更改后已更改”错误
【发布时间】:2017-03-26 13:28:37
【问题描述】:

在我的 Angular 2 应用程序中,我想要一个输入列表。在其中一个中按 Enter 将添加一个新输入并立即关注它。这是已经在本网站和Eric Martinez provided a neat answer to it that accomplishes that with a custom directive 上提出的问题。

他的解决方案是基于一个虚拟的整数列表。我在尝试使其适应更现实的场景时遇到了困难。我已经 fork Eric 的 plunk,所以你可以run the code here,但最重要的文件是这个:

//our root app component
import {Component, Directive, Renderer, ElementRef} from 'angular2/core'

class Person { name: string }

@Directive({
  selector : 'input'
})
class MyInput {
  constructor(public renderer: Renderer, public elementRef: ElementRef) {}

  // It won't work at construction time
  ngOnInit() {
    this.renderer.invokeElementMethod(
      this.elementRef.nativeElement, 'focus', []);
  }
}

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div *ngFor="#input of inputs">
      <input
        (keydown.enter)="add()" 
        [(ngModel)]="input.name"
        type="text"/>
    </div>
  `,
  directives: [MyInput]
})
export class App {
  inputs: Person[] = [{name: 'Alice'}];

  add() {
    var newPerson = new Person();
    newPerson.name = 'Bob';

    this.inputs.push(newPerson);
  }
}

我的inputs 数组现在是Person 对象的列表。输入双向绑定到Personname 属性。 &lt;input&gt; 现在被包裹在 &lt;div&gt; 中,因为我希望稍后我会编写更多标记来显示每个 Person

进行这些更改后,该示例仅在第一次尝试按 Enter 时起作用 - 一个带有文本 Bob 的新输入按预期出现。但是当我第二次尝试按 Enter 时,出现错误:

angular2.dev.js:23730 Error: Expression 'ngClassUntouched in App@2:6' has changed after it was checked. Previous value: 'true'. Current value: 'false'
    at ExpressionChangedAfterItHasBeenCheckedException.BaseException [as constructor] (angular2.dev.js:7587)
    at new ExpressionChangedAfterItHasBeenCheckedException (angular2.dev.js:4992)
    at ChangeDetector_App_1.AbstractChangeDetector.throwOnChangeError (angular2.dev.js:9989)
    at ChangeDetector_App_1.detectChangesInRecordsInternal (viewFactory_App:143)
    at ChangeDetector_App_1.AbstractChangeDetector.detectChangesInRecords (angular2.dev.js:9874)
    at ChangeDetector_App_1.AbstractChangeDetector.runDetectChanges (angular2.dev.js:9857)
    at ChangeDetector_App_0.AbstractChangeDetector._detectChangesContentChildren (angular2.dev.js:9930)
    at ChangeDetector_App_0.AbstractChangeDetector.runDetectChanges (angular2.dev.js:9858)
    at ChangeDetector_HostApp_0.AbstractChangeDetector._detectChangesInViewChildren (angular2.dev.js:9936)
    at ChangeDetector_HostApp_0.AbstractChangeDetector.runDetectChanges (angular2.dev.js:9861)

我该如何解决这个问题?

我正在 Chrome 中运行该示例。我发现使用基于 Angular2 beta 12 版本的 Eric Martinez 的 plunk 最容易演示该问题,但我遇到相同错误的实际应用程序当前使用的是 Angular 2.0.0。

【问题讨论】:

    标签: angular typescript angular2-directives


    【解决方案1】:

    Angular2 不喜欢在更改检测回调期间更改模型(例如 ngOnInit() 是)。调用ChangeDetectorRef.detectChanges() 应该可以解决它:

    class MyInput {
      constructor(public renderer: Renderer, public elementRef: ElementRef
          ,private cdRef:ChangeDetectorRef) {}
    
      // It won't work at construction time
      ngOnInit() {
        this.renderer.invokeElementMethod(
          this.elementRef.nativeElement, 'focus', []);
        this.cdRef.detectChanges();
      }
    }
    

    【讨论】:

    • 由于某种原因,在 Plunker 中执行此操作会导致 chrome 因内存不足错误而崩溃。在我的实际应用程序中,这可以防止出现任何新的&lt;input&gt;,即使仍然附加了底层数组。我也不太明白我以什么方式改变模型。
    • 无论如何你都不应该尝试关注ngOnInit(),因为这里的元素还不存在。使用ngAfterViewInit()ngAfterViewChecked()
    • 我尝试了这两个事件,我尝试将它们与.detectChanges()结合起来,仍然是同样的问题...plnkr.co/edit/Hh3H36fceFSm0Q20PVV3?p=preview
    • 我明白了。如果仅在 after 之后创建一个元素,则它是有意义的。当数组最初已经包含一个值列表时,我不会认为它是一个很好的解决方案。我让它与setTimeout() 一起工作。不知道为什么它不适用于detectChanges(),但我想这是因为错误不是来自MyInput,而是来自NgModel,它不受detectChanges() 的影响。如果您不喜欢setTimeout(),另一种可能有效的方法是注入ApplicationRef(而不是ChangeDetectorRef)并调用它的.tick()(而不是detectChanges()
    • 谢谢,我会选择 setTimeout() 解决方案 :) 如果没有旧的 setTimeout,我们会怎么做。
    猜你喜欢
    • 2016-10-19
    • 2017-05-08
    • 2018-05-11
    • 1970-01-01
    • 2019-05-14
    • 2017-06-23
    • 2017-07-08
    • 2019-01-13
    • 2016-07-19
    相关资源
    最近更新 更多