Skip to content

Commit

Permalink
fix(cdk): ensure stable zone before applying autocomplete (#11052)
Browse files Browse the repository at this point in the history
* fix(cdk): ensure stable zone before applying autocomplete

* fix(cdk): fix event passing
  • Loading branch information
N1XUS authored Dec 6, 2023
1 parent c97e71d commit fe6ea50
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { AutoCompleteDirective } from './auto-complete.directive';
import { DestroyedService } from '../../services';

@Component({
template: ` <input [options]="values" fd-auto-complete />`
Expand All @@ -20,7 +21,8 @@ describe('AutoCompleteDirective', () => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
imports: [AutoCompleteDirective]
imports: [AutoCompleteDirective],
providers: [DestroyedService]
}).compileComponents();
}));

Expand All @@ -40,11 +42,11 @@ describe('AutoCompleteDirective', () => {

directive.inputText = 'ap';

directive.handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'p' });
directive._handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'p' });

expect((<any>directive)._elementRef.nativeElement.value).toBe('Apple');

directive.handleKeyboardEvent(<any>{ stopPropagation: () => {}, preventDefault: () => {}, key: 'Enter' });
directive._handleKeyboardEvent(<any>{ stopPropagation: () => {}, preventDefault: () => {}, key: 'Enter' });

expect(spy).toHaveBeenCalledWith({
term: 'Apple',
Expand All @@ -57,11 +59,11 @@ describe('AutoCompleteDirective', () => {

directive.inputText = 'ap';

directive.handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'p' });
directive._handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'p' });

expect((<any>directive)._elementRef.nativeElement.value).toBe('Apple');

directive.handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'ArrowLeft' });
directive._handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'ArrowLeft' });

expect(spy).toHaveBeenCalledWith({
term: 'Apple',
Expand All @@ -72,20 +74,20 @@ describe('AutoCompleteDirective', () => {
it('should stop completing word', () => {
directive.inputText = 'ap';

directive.handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'p' });
directive._handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'p' });

expect((<any>directive)._elementRef.nativeElement.value).toBe('Apple');

directive.handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'Backspace' });
directive._handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'Backspace' });

expect((<any>directive)._elementRef.nativeElement.value).toBe('ap');
});

it('should not complete, when other word is written', () => {
directive.inputText = 'SomeOtherWord';

directive.handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'p' });
directive.handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'Escape' });
directive._handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'p' });
directive._handleKeyboardEvent(<any>{ preventDefault: () => {}, key: 'Escape' });

expect((<any>directive)._elementRef.nativeElement.value).toBe('SomeOtherWord');
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { Directive, ElementRef, EventEmitter, HostListener, Input, Output } from '@angular/core';
import { Directive, ElementRef, EventEmitter, Input, NgZone, Output } from '@angular/core';
import { BACKSPACE, CONTROL, DELETE, ENTER, ESCAPE, LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
import { KeyUtil } from '../../functions/key-util';
import {
DeprecatedSelector,
FD_DEPRECATED_DIRECTIVE_SELECTOR,
getDeprecatedModel
} from '../../deprecated-selector.class';
import { DestroyedService } from '../../services';
import { first, fromEvent, map, switchMap, takeUntil } from 'rxjs';

export interface AutoCompleteEvent {
term: string;
Expand All @@ -18,6 +20,7 @@ export interface AutoCompleteEvent {
selector: '[fdAutoComplete], [fd-auto-complete]',
standalone: true,
providers: [
DestroyedService,
{
provide: FD_DEPRECATED_DIRECTIVE_SELECTOR,
useValue: getDeprecatedModel('[fdkAutoComplete]', '[fdAutoComplete], [fd-auto-complete]')
Expand Down Expand Up @@ -78,11 +81,39 @@ export class AutoCompleteDirective {
private lastKeyUpEvent: KeyboardEvent;

/** @hidden */
constructor(private _elementRef: ElementRef) {}
// eslint-disable-next-line @typescript-eslint/member-ordering
constructor(
private readonly _elementRef: ElementRef,
private readonly _zone: NgZone,
private readonly _destroy$: DestroyedService
) {
/**
* Fixes #10710
* WIth chinese characters inputText property update was triggered after the keyup event trigger.
* By ensuring that we set all properties we can proceed with stable data.
*/
this._zone.runOutsideAngular(() => {
const keyupEvent = fromEvent<KeyboardEvent>(this._elementRef.nativeElement, 'keyup');

keyupEvent
.pipe(
switchMap((evt) =>
this._zone.onStable.pipe(
first(),
map(() => evt)
)
),
takeUntil(this._destroy$)
)
.subscribe((evt) => {
this._handleKeyboardEvent(evt);
console.log(this.inputText);
});
});
}

/** @hidden */
@HostListener('keyup', ['$event'])
handleKeyboardEvent(event: KeyboardEvent): void {
_handleKeyboardEvent(event: KeyboardEvent): void {
if (this.enable) {
if (KeyUtil.isKeyCode(event, this._stopKeys)) {
this._elementRef.nativeElement.value = this.inputText;
Expand Down

0 comments on commit fe6ea50

Please sign in to comment.