In my Angular 2 application, I want to have a list of inputs. Pressing Enter inside one of them would add a new input and immediately focus on it. This is a question that has already been asked on this site and Eric Martinez provided a neat answer to it that accomplishes that with a custom directive.
His solution is based on a dummy list of integers. I am having difficulties trying to adapt it to a more realistic scenario. I have forked Eric's plunk, so you can run the code here, but the most important file is this one:
//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);
}
}
My array of inputs
is now a list of Person
objects. The inputs are bound bidirectionally to the name
property of a Person
. The <input>
is now wrapped inside a <div>
, as I expect that later I will write more markup to display each Person
.
After making these changes, the example works only at the first attempt of pressing Enter - a new input with text Bob appears as expected. But when I then try to press Enter for the second time, I get an error:
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)
How can I fix that?
I am running the example in Chrome. I found it easiest to demostrate the problem using Eric Martinez's plunks that are based on beta 12 version of Angular2 but my real world application where I get the same error is currently using Angular 2.0.0.
Angular2 doesn't like when the model is changed during a change detection callback (like ngOnInit()
is). Calling ChangeDetectorRef.detectChanges()
should fix it:
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With