Skip to content

Commit eaa79ef

Browse files
committed
fix(overlay): make config immutable for existing refs
This changes makes the OverlayConfig instance in OverlatRef immutable. Calling `getConfig` will now return a clone of the config to make clear that it cannot be modified directly to change the state of the overlay. This also updates the `updateSize` method to accept a partial config to apply to the existing config (and make a new config instance). This *also* tightens up the typings on Portal.attach BREAKING CHANGE: OverlayRef.getConfig returns an immutable version of the config object. BREAKING CHANGE: OverlayRef.updateSize now accepts a OverlaySizeConfig rather than being based on the existing config object.
1 parent a0dac05 commit eaa79ef

File tree

4 files changed

+63
-31
lines changed

4 files changed

+63
-31
lines changed

src/cdk/overlay/overlay-directives.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
337337
}
338338

339339
this._position.withDirection(this.dir);
340-
this._overlayRef.getConfig().direction = this.dir;
340+
this._overlayRef.setDirection(this.dir);
341341
this._initEscapeListener();
342342

343343
if (!this._overlayRef.hasAttached()) {

src/cdk/overlay/overlay-ref.ts

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {NgZone} from '@angular/core';
10-
import {PortalOutlet, Portal} from '@angular/cdk/portal';
11-
import {OverlayConfig} from './overlay-config';
12-
import {OverlayKeyboardDispatcher} from './keyboard/overlay-keyboard-dispatcher';
9+
import {Direction} from '@angular/cdk/bidi';
10+
import {ComponentPortal, Portal, PortalOutlet, TemplatePortal} from '@angular/cdk/portal';
11+
import {ComponentRef, EmbeddedViewRef, NgZone} from '@angular/core';
1312
import {Observable} from 'rxjs/Observable';
14-
import {Subject} from 'rxjs/Subject';
1513
import {first} from 'rxjs/operators/first';
14+
import {Subject} from 'rxjs/Subject';
15+
import {OverlayKeyboardDispatcher} from './keyboard/overlay-keyboard-dispatcher';
16+
import {OverlayConfig} from './overlay-config';
17+
1618

19+
/** An object where all of its properties cannot be written. */
20+
export type ImmutableObject<T> = {
21+
readonly [P in keyof T]: T[P];
22+
};
1723

1824
/**
1925
* Reference to an overlay that has been created with the Overlay service.
@@ -31,7 +37,7 @@ export class OverlayRef implements PortalOutlet {
3137
constructor(
3238
private _portalOutlet: PortalOutlet,
3339
private _pane: HTMLElement,
34-
private _config: OverlayConfig,
40+
private _config: ImmutableObject<OverlayConfig>,
3541
private _ngZone: NgZone,
3642
private _keyboardDispatcher: OverlayKeyboardDispatcher) {
3743

@@ -45,8 +51,14 @@ export class OverlayRef implements PortalOutlet {
4551
return this._pane;
4652
}
4753

54+
attach<T>(portal: ComponentPortal<T>): ComponentRef<T>;
55+
attach<T>(portal: TemplatePortal<T>): EmbeddedViewRef<T>;
56+
attach(portal: any): any;
57+
4858
/**
49-
* Attaches the overlay to a portal instance and adds the backdrop.
59+
* Attaches content, given via a Portal, to the overlay.
60+
* If the overlay is configured to have a backdrop, it will be created.
61+
*
5062
* @param portal Portal instance to which to attach the overlay.
5163
* @returns The portal attachment result.
5264
*/
@@ -59,8 +71,8 @@ export class OverlayRef implements PortalOutlet {
5971

6072
// Update the pane element with the given configuration.
6173
this._updateStackingOrder();
62-
this.updateSize();
63-
this.updateDirection();
74+
this._updateElementSize();
75+
this._updateElementDirection();
6476

6577
if (this._config.scrollStrategy) {
6678
this._config.scrollStrategy.enable();
@@ -133,9 +145,7 @@ export class OverlayRef implements PortalOutlet {
133145
return detachmentResult;
134146
}
135147

136-
/**
137-
* Cleans up the overlay from the DOM.
138-
*/
148+
/** Cleans up the overlay from the DOM. */
139149
dispose(): void {
140150
const isAttached = this.hasAttached();
141151

@@ -159,16 +169,12 @@ export class OverlayRef implements PortalOutlet {
159169
this._detachments.complete();
160170
}
161171

162-
/**
163-
* Checks whether the overlay has been attached.
164-
*/
172+
/** Whether the overlay has attached content. */
165173
hasAttached(): boolean {
166174
return this._portalOutlet.hasAttached();
167175
}
168176

169-
/**
170-
* Gets an observable that emits when the backdrop has been clicked.
171-
*/
177+
/** Gets an observable that emits when the backdrop has been clicked. */
172178
backdropClick(): Observable<void> {
173179
return this._backdropClick.asObservable();
174180
}
@@ -188,9 +194,7 @@ export class OverlayRef implements PortalOutlet {
188194
return this._keydownEvents.asObservable();
189195
}
190196

191-
/**
192-
* Gets the current config of the overlay.
193-
*/
197+
/** Gets the the current overlay configuration, which is immutable. */
194198
getConfig(): OverlayConfig {
195199
return this._config;
196200
}
@@ -202,13 +206,25 @@ export class OverlayRef implements PortalOutlet {
202206
}
203207
}
204208

209+
/** Update the size properties of the overlay. */
210+
updateSize(sizeConfig: OverlaySizeConfig) {
211+
this._config = {...this._config, ...sizeConfig};
212+
this._updateElementSize();
213+
}
214+
215+
/** Sets the LTR/RTL direction for the overlay. */
216+
setDirection(dir: Direction) {
217+
this._config = {...this._config, direction: dir};
218+
this._updateElementDirection();
219+
}
220+
205221
/** Updates the text direction of the overlay panel. */
206-
private updateDirection() {
222+
private _updateElementDirection() {
207223
this._pane.setAttribute('dir', this._config.direction!);
208224
}
209225

210-
/** Updates the size of the overlay based on the overlay config. */
211-
updateSize() {
226+
/** Updates the size of the overlay element based on the overlay config. */
227+
private _updateElementSize() {
212228
if (this._config.width || this._config.width === 0) {
213229
this._pane.style.width = formatCssUnit(this._config.width);
214230
}
@@ -257,10 +273,12 @@ export class OverlayRef implements PortalOutlet {
257273
this._backdropElement.addEventListener('click', () => this._backdropClick.next(null));
258274

259275
// Add class to fade-in the backdrop after one frame.
260-
requestAnimationFrame(() => {
261-
if (this._backdropElement) {
262-
this._backdropElement.classList.add('cdk-overlay-backdrop-showing');
263-
}
276+
this._ngZone.runOutsideAngular(() => {
277+
requestAnimationFrame(() => {
278+
if (this._backdropElement) {
279+
this._backdropElement.classList.add('cdk-overlay-backdrop-showing');
280+
}
281+
});
264282
});
265283
}
266284

@@ -321,3 +339,14 @@ export class OverlayRef implements PortalOutlet {
321339
function formatCssUnit(value: number | string) {
322340
return typeof value === 'string' ? value as string : `${value}px`;
323341
}
342+
343+
344+
/** Size properties for an overlay. */
345+
export interface OverlaySizeConfig {
346+
width?: number | string;
347+
height?: number | string;
348+
minWidth?: number | string;
349+
minHeight?: number | string;
350+
maxWidth?: number | string;
351+
maxHeight?: number | string;
352+
}

src/cdk/portal/portal.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ export abstract class BasePortalOutlet implements PortalOutlet {
179179
return !!this._attachedPortal;
180180
}
181181

182+
attach<T>(portal: ComponentPortal<T>): ComponentRef<T>;
183+
attach<T>(portal: TemplatePortal<T>): EmbeddedViewRef<T>;
184+
attach(portal: any): any;
185+
182186
/** Attaches a portal. */
183187
attach(portal: Portal<any>): any {
184188
if (!portal) {

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
458458
this._overlayRef = this._overlay.create(this._getOverlayConfig());
459459
} else {
460460
/** Update the panel width, in case the host width has changed */
461-
this._overlayRef.getConfig().width = this._getHostWidth();
462-
this._overlayRef.updateSize();
461+
this._overlayRef.updateSize({width: this._getHostWidth()});
463462
}
464463

465464
if (this._overlayRef && !this._overlayRef.hasAttached()) {

0 commit comments

Comments
 (0)