From efd348512f1ac08136d9b41d48cd71ccf6b35640 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 19 Apr 2017 00:55:08 +0200 Subject: [PATCH] fix(autocomplete): panel not being shown with delay and OnPush change detection (#3977) Fixes #3955. --- src/lib/autocomplete/autocomplete.spec.ts | 55 ++++++++++++++++++++++- src/lib/autocomplete/autocomplete.ts | 10 ++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index f7ca92a28b8a..29e21dca8232 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -1,5 +1,13 @@ import {TestBed, async, fakeAsync, tick, ComponentFixture} from '@angular/core/testing'; -import {Component, OnDestroy, QueryList, ViewChild, ViewChildren} from '@angular/core'; +import { + Component, + OnDestroy, + QueryList, + ViewChild, + ViewChildren, + ChangeDetectionStrategy, + OnInit, +} from '@angular/core'; import {By} from '@angular/platform-browser'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {MdAutocompleteModule, MdAutocompleteTrigger} from './index'; @@ -38,7 +46,8 @@ describe('MdAutocomplete', () => { SimpleAutocomplete, AutocompleteWithoutForms, NgIfAutocomplete, - AutocompleteWithNgModel + AutocompleteWithNgModel, + AutocompleteWithOnPushDelay ], providers: [ {provide: OverlayContainer, useFactory: () => { @@ -1090,6 +1099,24 @@ describe('MdAutocomplete', () => { expect(Math.ceil(parseFloat(overlayPane.style.width))).toEqual(500); }); + + it('should show the panel when the options are initialized later within a component with ' + + 'OnPush change detection', fakeAsync(() => { + let fixture = TestBed.createComponent(AutocompleteWithOnPushDelay); + + fixture.detectChanges(); + dispatchFakeEvent(fixture.debugElement.query(By.css('input')).nativeElement, 'focus'); + tick(1000); + fixture.detectChanges(); + + Promise.resolve().then(() => { + let panel = overlayContainerElement.querySelector('.mat-autocomplete-panel') as HTMLElement; + let visibleClass = 'mat-autocomplete-visible'; + + fixture.detectChanges(); + expect(panel.classList).toContain(visibleClass, `Expected panel to be visible.`); + }); + })); }); @Component({ @@ -1239,6 +1266,30 @@ class AutocompleteWithNgModel { } + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + + + + + {{ option }} + + ` +}) +class AutocompleteWithOnPushDelay implements OnInit { + options: string[]; + + ngOnInit() { + setTimeout(() => { + this.options = ['One']; + }, 1000); + } +} + + /** This is a mock keyboard event to test keyboard events in the autocomplete. */ class MockKeyboardEvent { constructor(public keyCode: number) {} diff --git a/src/lib/autocomplete/autocomplete.ts b/src/lib/autocomplete/autocomplete.ts index f516c80c532c..520a0aa33329 100644 --- a/src/lib/autocomplete/autocomplete.ts +++ b/src/lib/autocomplete/autocomplete.ts @@ -7,7 +7,8 @@ import { QueryList, TemplateRef, ViewChild, - ViewEncapsulation + ViewEncapsulation, + ChangeDetectorRef, } from '@angular/core'; import {MdOption} from '../core'; import {ActiveDescendantKeyManager} from '../core/a11y/activedescendant-key-manager'; @@ -52,6 +53,8 @@ export class MdAutocomplete implements AfterContentInit { /** Unique ID to be used by autocomplete trigger's "aria-owns" property. */ id: string = `md-autocomplete-${_uniqueAutocompleteIdCounter++}`; + constructor(private _changeDetectorRef: ChangeDetectorRef) { } + ngAfterContentInit() { this._keyManager = new ActiveDescendantKeyManager(this.options).withWrap(); } @@ -68,7 +71,10 @@ export class MdAutocomplete implements AfterContentInit { /** Panel should hide itself when the option list is empty. */ _setVisibility() { - Promise.resolve().then(() => this.showPanel = !!this.options.length); + Promise.resolve().then(() => { + this.showPanel = !!this.options.length; + this._changeDetectorRef.markForCheck(); + }); } /** Sets a class on the panel based on its position (used to set y-offset). */