diff --git a/src/lib/core/overlay/overlay-directives.spec.ts b/src/lib/core/overlay/overlay-directives.spec.ts index 7cb58b1ff26d..2daece1e33d4 100644 --- a/src/lib/core/overlay/overlay-directives.spec.ts +++ b/src/lib/core/overlay/overlay-directives.spec.ts @@ -1,7 +1,7 @@ -import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {ComponentFixture, TestBed, async} from '@angular/core/testing'; import {Component, ViewChild} from '@angular/core'; import {By} from '@angular/platform-browser'; -import {ConnectedOverlayDirective, OverlayModule} from './overlay-directives'; +import {ConnectedOverlayDirective, OverlayModule, OverlayOrigin} from './overlay-directives'; import {OverlayContainer} from './overlay-container'; import {ConnectedPositionStrategy} from './position/connected-position-strategy'; import {ConnectedOverlayPositionChange} from './position/connected-position'; @@ -18,7 +18,7 @@ describe('Overlay directives', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [OverlayModule], - declarations: [ConnectedOverlayDirectiveTest], + declarations: [ConnectedOverlayDirectiveTest, ConnectedOverlayPropertyInitOrder], providers: [ {provide: OverlayContainer, useFactory: () => { overlayContainerElement = document.createElement('div'); @@ -111,6 +111,21 @@ describe('Overlay directives', () => { 'Expected overlay to have been detached.'); }); + it('should not depend on the order in which the `origin` and `open` are set', async(() => { + fixture.destroy(); + + const propOrderFixture = TestBed.createComponent(ConnectedOverlayPropertyInitOrder); + propOrderFixture.detectChanges(); + + const overlayDirective = propOrderFixture.componentInstance.connectedOverlayDirective; + + expect(() => { + overlayDirective.open = true; + overlayDirective.origin = propOrderFixture.componentInstance.trigger; + propOrderFixture.detectChanges(); + }).not.toThrow(); + })); + describe('inputs', () => { it('should set the width', () => { @@ -310,3 +325,14 @@ class ConnectedOverlayDirectiveTest { @ViewChild(ConnectedOverlayDirective) connectedOverlayDirective: ConnectedOverlayDirective; } + +@Component({ + template: ` + + Menu content`, +}) +class ConnectedOverlayPropertyInitOrder { + @ViewChild(ConnectedOverlayDirective) connectedOverlayDirective: ConnectedOverlayDirective; + @ViewChild('trigger') trigger: OverlayOrigin; +} + diff --git a/src/lib/core/overlay/overlay-directives.ts b/src/lib/core/overlay/overlay-directives.ts index b3ab073e02e6..aefd5c200795 100644 --- a/src/lib/core/overlay/overlay-directives.ts +++ b/src/lib/core/overlay/overlay-directives.ts @@ -10,6 +10,8 @@ import { Output, ElementRef, Renderer2, + OnChanges, + SimpleChanges, } from '@angular/core'; import {Overlay, OVERLAY_PROVIDERS} from './overlay'; import {OverlayRef} from './overlay-ref'; @@ -63,10 +65,9 @@ export class OverlayOrigin { selector: '[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]', exportAs: 'cdkConnectedOverlay' }) -export class ConnectedOverlayDirective implements OnDestroy { +export class ConnectedOverlayDirective implements OnDestroy, OnChanges { private _overlayRef: OverlayRef; private _templatePortal: TemplatePortal; - private _open = false; private _hasBackdrop = false; private _backdropSubscription: Subscription; private _positionSubscription: Subscription; @@ -125,6 +126,9 @@ export class ConnectedOverlayDirective implements OnDestroy { /** Strategy to be used when handling scroll events while the overlay is open. */ @Input() scrollStrategy: ScrollStrategy = new RepositionScrollStrategy(this._scrollDispatcher); + /** Whether the overlay is open. */ + @Input() open: boolean = false; + /** Whether or not the overlay should attach a backdrop. */ @Input() get hasBackdrop() { @@ -135,16 +139,6 @@ export class ConnectedOverlayDirective implements OnDestroy { this._hasBackdrop = coerceBooleanProperty(value); } - @Input() - get open() { - return this._open; - } - - set open(value: boolean) { - value ? this._attachOverlay() : this._detachOverlay(); - this._open = value; - } - /** Event emitted when the backdrop is clicked. */ @Output() backdropClick = new EventEmitter(); @@ -183,6 +177,12 @@ export class ConnectedOverlayDirective implements OnDestroy { this._destroyOverlay(); } + ngOnChanges(changes: SimpleChanges) { + if (changes['open']) { + this.open ? this._attachOverlay() : this._detachOverlay(); + } + } + /** Creates an overlay */ private _createOverlay() { if (!this.positions || !this.positions.length) {