Skip to content

Commit 2dbc766

Browse files
jelbourntinayuangao
authored andcommitted
fix(overlay): make config immutable for existing refs (#7376)
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 23c09b4 commit 2dbc766

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
@@ -333,7 +333,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
333333
}
334334

335335
this._position.withDirection(this.dir);
336-
this._overlayRef.getConfig().direction = this.dir;
336+
this._overlayRef.setDirection(this.dir);
337337
this._document.addEventListener('keydown', this._escapeListener);
338338

339339
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 {take} from 'rxjs/operators/take';
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

@@ -161,16 +171,12 @@ export class OverlayRef implements PortalOutlet {
161171
this._detachments.complete();
162172
}
163173

164-
/**
165-
* Checks whether the overlay has been attached.
166-
*/
174+
/** Whether the overlay has attached content. */
167175
hasAttached(): boolean {
168176
return this._portalOutlet.hasAttached();
169177
}
170178

171-
/**
172-
* Gets an observable that emits when the backdrop has been clicked.
173-
*/
179+
/** Gets an observable that emits when the backdrop has been clicked. */
174180
backdropClick(): Observable<void> {
175181
return this._backdropClick.asObservable();
176182
}
@@ -190,9 +196,7 @@ export class OverlayRef implements PortalOutlet {
190196
return this._keydownEvents.asObservable();
191197
}
192198

193-
/**
194-
* Gets the current config of the overlay.
195-
*/
199+
/** Gets the the current overlay configuration, which is immutable. */
196200
getConfig(): OverlayConfig {
197201
return this._config;
198202
}
@@ -204,13 +208,25 @@ export class OverlayRef implements PortalOutlet {
204208
}
205209
}
206210

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

212-
/** Updates the size of the overlay based on the overlay config. */
213-
updateSize() {
228+
/** Updates the size of the overlay element based on the overlay config. */
229+
private _updateElementSize() {
214230
if (this._config.width || this._config.width === 0) {
215231
this._pane.style.width = formatCssUnit(this._config.width);
216232
}
@@ -259,10 +275,12 @@ export class OverlayRef implements PortalOutlet {
259275
this._backdropElement.addEventListener('click', () => this._backdropClick.next(null));
260276

261277
// Add class to fade-in the backdrop after one frame.
262-
requestAnimationFrame(() => {
263-
if (this._backdropElement) {
264-
this._backdropElement.classList.add('cdk-overlay-backdrop-showing');
265-
}
278+
this._ngZone.runOutsideAngular(() => {
279+
requestAnimationFrame(() => {
280+
if (this._backdropElement) {
281+
this._backdropElement.classList.add('cdk-overlay-backdrop-showing');
282+
}
283+
});
266284
});
267285
}
268286

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

src/cdk/portal/portal.ts

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

184+
attach<T>(portal: ComponentPortal<T>): ComponentRef<T>;
185+
attach<T>(portal: TemplatePortal<T>): EmbeddedViewRef<T>;
186+
attach(portal: any): any;
187+
184188
/** Attaches a portal. */
185189
attach(portal: Portal<any>): any {
186190
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)