From c5ea03a023ccece0c55158ac274e9f9aa7b47e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=A7=E8=A1=A1?= Date: Thu, 12 Jan 2017 20:02:45 +0800 Subject: [PATCH] feat(ngModel):handle ime events (#13891) PR Close: #13891 --- .../@angular/forms/src/directives/ng_model.ts | 14 ++++++-- .../forms/test/template_integration_spec.ts | 33 +++++++++++++++++++ tools/public_api_guard/forms/index.d.ts | 2 ++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/modules/@angular/forms/src/directives/ng_model.ts b/modules/@angular/forms/src/directives/ng_model.ts index 31e3b96245..2c0debe45b 100644 --- a/modules/@angular/forms/src/directives/ng_model.ts +++ b/modules/@angular/forms/src/directives/ng_model.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; +import {Directive, Host, HostListener, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; import {EventEmitter} from '../facade/async'; import {FormControl} from '../model'; @@ -115,6 +115,7 @@ export class NgModel extends NgControl implements OnChanges, _control = new FormControl(); /** @internal */ _registered = false; + private _composing = false; viewModel: any; @Input() name: string; @@ -124,6 +125,15 @@ export class NgModel extends NgControl implements OnChanges, @Output('ngModelChange') update = new EventEmitter(); + @HostListener('compositionstart') + compositionStart(): void { this._composing = true; } + + @HostListener('compositionend') + compositionEnd(): void { + this._composing = false; + this.update.emit(this.viewModel); + } + constructor(@Optional() @Host() parent: ControlContainer, @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array, @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array, @@ -167,7 +177,7 @@ export class NgModel extends NgControl implements OnChanges, viewToModelUpdate(newValue: any): void { this.viewModel = newValue; - this.update.emit(newValue); + !this._composing && this.update.emit(newValue); } private _setUpControl(): void { diff --git a/modules/@angular/forms/test/template_integration_spec.ts b/modules/@angular/forms/test/template_integration_spec.ts index c94bc7f368..f753b91ebd 100644 --- a/modules/@angular/forms/test/template_integration_spec.ts +++ b/modules/@angular/forms/test/template_integration_spec.ts @@ -42,6 +42,39 @@ export function main() { expect(fixture.componentInstance.name).toEqual('updatedValue'); })); + it('should ngModel hold ime events until compositionend', fakeAsync(() => { + const fixture = TestBed.createComponent(StandaloneNgModel); + // model -> view + const inputEl = fixture.debugElement.query(By.css('input')); + const inputNativeEl = inputEl.nativeElement; + + fixture.componentInstance.name = 'oldValue'; + + fixture.detectChanges(); + tick(); + + expect(inputNativeEl.value).toEqual('oldValue'); + // view -> model + inputEl.triggerEventHandler('compositionstart', null); + + inputNativeEl.value = 'updatedValue'; + dispatchEvent(inputNativeEl, 'input'); + tick(); + + // should ngModel not update when compositionstart + + expect(fixture.componentInstance.name).toEqual('oldValue'); + + inputEl.triggerEventHandler('compositionend', null); + + fixture.detectChanges(); + tick(); + + // should ngModel update when compositionend + + expect(fixture.componentInstance.name).toEqual('updatedValue'); + })); + it('should support ngModel registration with a parent form', fakeAsync(() => { const fixture = initTest(NgModelForm); fixture.componentInstance.name = 'Nancy'; diff --git a/tools/public_api_guard/forms/index.d.ts b/tools/public_api_guard/forms/index.d.ts index b52e5dd59e..5d0f1166e3 100644 --- a/tools/public_api_guard/forms/index.d.ts +++ b/tools/public_api_guard/forms/index.d.ts @@ -435,6 +435,8 @@ export declare class NgModel extends NgControl implements OnChanges, OnDestroy { validator: ValidatorFn; viewModel: any; constructor(parent: ControlContainer, validators: Array, asyncValidators: Array, valueAccessors: ControlValueAccessor[]); + compositionEnd(): void; + compositionStart(): void; ngOnChanges(changes: SimpleChanges): void; ngOnDestroy(): void; viewToModelUpdate(newValue: any): void;