Skip to content

Commit efd3485

Browse files
crisbetokara
authored andcommitted
fix(autocomplete): panel not being shown with delay and OnPush change detection (#3977)
Fixes #3955.
1 parent 4bfd2a3 commit efd3485

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import {TestBed, async, fakeAsync, tick, ComponentFixture} from '@angular/core/testing';
2-
import {Component, OnDestroy, QueryList, ViewChild, ViewChildren} from '@angular/core';
2+
import {
3+
Component,
4+
OnDestroy,
5+
QueryList,
6+
ViewChild,
7+
ViewChildren,
8+
ChangeDetectionStrategy,
9+
OnInit,
10+
} from '@angular/core';
311
import {By} from '@angular/platform-browser';
412
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
513
import {MdAutocompleteModule, MdAutocompleteTrigger} from './index';
@@ -38,7 +46,8 @@ describe('MdAutocomplete', () => {
3846
SimpleAutocomplete,
3947
AutocompleteWithoutForms,
4048
NgIfAutocomplete,
41-
AutocompleteWithNgModel
49+
AutocompleteWithNgModel,
50+
AutocompleteWithOnPushDelay
4251
],
4352
providers: [
4453
{provide: OverlayContainer, useFactory: () => {
@@ -1090,6 +1099,24 @@ describe('MdAutocomplete', () => {
10901099
expect(Math.ceil(parseFloat(overlayPane.style.width))).toEqual(500);
10911100

10921101
});
1102+
1103+
it('should show the panel when the options are initialized later within a component with ' +
1104+
'OnPush change detection', fakeAsync(() => {
1105+
let fixture = TestBed.createComponent(AutocompleteWithOnPushDelay);
1106+
1107+
fixture.detectChanges();
1108+
dispatchFakeEvent(fixture.debugElement.query(By.css('input')).nativeElement, 'focus');
1109+
tick(1000);
1110+
fixture.detectChanges();
1111+
1112+
Promise.resolve().then(() => {
1113+
let panel = overlayContainerElement.querySelector('.mat-autocomplete-panel') as HTMLElement;
1114+
let visibleClass = 'mat-autocomplete-visible';
1115+
1116+
fixture.detectChanges();
1117+
expect(panel.classList).toContain(visibleClass, `Expected panel to be visible.`);
1118+
});
1119+
}));
10931120
});
10941121

10951122
@Component({
@@ -1239,6 +1266,30 @@ class AutocompleteWithNgModel {
12391266

12401267
}
12411268

1269+
1270+
@Component({
1271+
changeDetection: ChangeDetectionStrategy.OnPush,
1272+
template: `
1273+
<md-input-container>
1274+
<input type="text" mdInput [mdAutocomplete]="auto">
1275+
</md-input-container>
1276+
1277+
<md-autocomplete #auto="mdAutocomplete">
1278+
<md-option *ngFor="let option of options" [value]="option">{{ option }}</md-option>
1279+
</md-autocomplete>
1280+
`
1281+
})
1282+
class AutocompleteWithOnPushDelay implements OnInit {
1283+
options: string[];
1284+
1285+
ngOnInit() {
1286+
setTimeout(() => {
1287+
this.options = ['One'];
1288+
}, 1000);
1289+
}
1290+
}
1291+
1292+
12421293
/** This is a mock keyboard event to test keyboard events in the autocomplete. */
12431294
class MockKeyboardEvent {
12441295
constructor(public keyCode: number) {}

src/lib/autocomplete/autocomplete.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
QueryList,
88
TemplateRef,
99
ViewChild,
10-
ViewEncapsulation
10+
ViewEncapsulation,
11+
ChangeDetectorRef,
1112
} from '@angular/core';
1213
import {MdOption} from '../core';
1314
import {ActiveDescendantKeyManager} from '../core/a11y/activedescendant-key-manager';
@@ -52,6 +53,8 @@ export class MdAutocomplete implements AfterContentInit {
5253
/** Unique ID to be used by autocomplete trigger's "aria-owns" property. */
5354
id: string = `md-autocomplete-${_uniqueAutocompleteIdCounter++}`;
5455

56+
constructor(private _changeDetectorRef: ChangeDetectorRef) { }
57+
5558
ngAfterContentInit() {
5659
this._keyManager = new ActiveDescendantKeyManager(this.options).withWrap();
5760
}
@@ -68,7 +71,10 @@ export class MdAutocomplete implements AfterContentInit {
6871

6972
/** Panel should hide itself when the option list is empty. */
7073
_setVisibility() {
71-
Promise.resolve().then(() => this.showPanel = !!this.options.length);
74+
Promise.resolve().then(() => {
75+
this.showPanel = !!this.options.length;
76+
this._changeDetectorRef.markForCheck();
77+
});
7278
}
7379

7480
/** Sets a class on the panel based on its position (used to set y-offset). */

0 commit comments

Comments
 (0)