Angular 2: изменение значения фокуса ввода "изменилось после его изменения".

В моем приложении Angular 2 я хочу иметь список входов. Нажатие Enter внутри одного из них добавит новый вход и сразу же сосредоточится на нем. Это вопрос, который уже задан на этом сайте, и Эрик Мартинес дал аккуратный ответ, который выполняет это с помощью настраиваемой директивы.

Его решение основано на фиктивном списке целых чисел. У меня возникают трудности с его адаптацией к более реалистичному сценарию. Я разветкил Eric plunk, так что вы можете запустить здесь код, но самым важным файлом является этот:

//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. Входы привязаны двунаправленно к свойству name a Person. <input> теперь завернут внутри <div>, так как я ожидаю, что позже я напишу больше разметки для отображения каждого Person.

После внесения этих изменений пример работает только при первой попытке нажатия Enter - новый ввод с текстом Bob выглядит так, как ожидалось. Но когда я пытаюсь нажать Enter во второй раз, я получаю сообщение об ошибке:

angular2.dev.js:23730 Error: Expression 'ngClassUntouched in [email protected]: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. Мне было проще проанализировать проблему с использованием плинков Eric Martinez, основанных на версии бета-версии Angular2, но мое приложение реального мира, где я получаю ту же ошибку, в настоящее время использует Angular 2.0.0.

Ответ 1

Angular2 не нравится, когда модель изменяется во время обратного вызова обнаружения изменения (например, ngOnInit() is). Вызов 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();
  }
}