Skip to content

Commit d0e178b

Browse files
authored
fix(material/core): stop manually instantiating MatRipple directive (#29630)
Previously we had to manually instantiate `MatRipple` in order to maintain backwards compatibility, but it was problematic because it would break whenever we tried to use DI in `MatRipple` and it prevented us from switching to the `inject` function. Instantiating `MatRipple` is no longer necessary after #29622 so these changes switch to creating an internal `RippleRenderer` instead.
1 parent 485bd99 commit d0e178b

File tree

2 files changed

+56
-46
lines changed

2 files changed

+56
-46
lines changed

src/material/core/private/ripple-loader.ts

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@
77
*/
88

99
import {DOCUMENT} from '@angular/common';
10+
import {ANIMATION_MODULE_TYPE, Injectable, NgZone, OnDestroy, inject} from '@angular/core';
1011
import {
11-
ANIMATION_MODULE_TYPE,
12-
ElementRef,
13-
Injectable,
14-
NgZone,
15-
OnDestroy,
16-
inject,
17-
} from '@angular/core';
18-
import {MAT_RIPPLE_GLOBAL_OPTIONS, MatRipple} from '../ripple';
12+
MAT_RIPPLE_GLOBAL_OPTIONS,
13+
RippleRenderer,
14+
RippleTarget,
15+
defaultRippleAnimationConfig,
16+
} from '../ripple';
1917
import {Platform, _getEventTarget} from '@angular/cdk/platform';
2018

2119
/** The options for the MatRippleLoader's event listeners. */
@@ -55,7 +53,10 @@ export class MatRippleLoader implements OnDestroy {
5553
private _globalRippleOptions = inject(MAT_RIPPLE_GLOBAL_OPTIONS, {optional: true});
5654
private _platform = inject(Platform);
5755
private _ngZone = inject(NgZone);
58-
private _hosts = new Map<HTMLElement, MatRipple>();
56+
private _hosts = new Map<
57+
HTMLElement,
58+
{renderer: RippleRenderer; target: RippleTarget; hasSetUpEvents: boolean}
59+
>();
5960

6061
constructor() {
6162
this._ngZone.runOutsideAngular(() => {
@@ -65,7 +66,7 @@ export class MatRippleLoader implements OnDestroy {
6566
});
6667
}
6768

68-
ngOnDestroy() {
69+
ngOnDestroy(): void {
6970
const hosts = this._hosts.keys();
7071

7172
for (const host of hosts) {
@@ -115,13 +116,15 @@ export class MatRippleLoader implements OnDestroy {
115116

116117
// If the ripple has already been instantiated, just disable it.
117118
if (ripple) {
118-
ripple.disabled = disabled;
119-
return;
120-
}
119+
ripple.target.rippleDisabled = disabled;
121120

122-
// Otherwise, set an attribute so we know what the
123-
// disabled state should be when the ripple is initialized.
124-
if (disabled) {
121+
if (!disabled && !ripple.hasSetUpEvents) {
122+
ripple.hasSetUpEvents = true;
123+
ripple.renderer.setupTriggerEvents(host);
124+
}
125+
} else if (disabled) {
126+
// Otherwise, set an attribute so we know what the
127+
// disabled state should be when the ripple is initialized.
125128
host.setAttribute(matRippleDisabled, '');
126129
} else {
127130
host.removeAttribute(matRippleDisabled);
@@ -148,50 +151,59 @@ export class MatRippleLoader implements OnDestroy {
148151
};
149152

150153
/** Creates a MatRipple and appends it to the given element. */
151-
private _createRipple(host: HTMLElement): MatRipple | undefined {
152-
if (!this._document) {
154+
private _createRipple(host: HTMLElement): void {
155+
if (!this._document || this._hosts.has(host)) {
153156
return;
154157
}
155158

156-
const existingRipple = this._hosts.get(host);
157-
if (existingRipple) {
158-
return existingRipple;
159-
}
160-
161159
// Create the ripple element.
162160
host.querySelector('.mat-ripple')?.remove();
163161
const rippleEl = this._document.createElement('span');
164162
rippleEl.classList.add('mat-ripple', host.getAttribute(matRippleClassName)!);
165163
host.append(rippleEl);
166164

167-
// Create the MatRipple.
168-
const ripple = new MatRipple(
169-
new ElementRef(rippleEl),
170-
this._ngZone,
171-
this._platform,
172-
this._globalRippleOptions ? this._globalRippleOptions : undefined,
173-
this._animationMode ? this._animationMode : undefined,
174-
);
175-
ripple._isInitialized = true;
176-
ripple.trigger = host;
177-
ripple.centered = host.hasAttribute(matRippleCentered);
178-
ripple.disabled = host.hasAttribute(matRippleDisabled);
179-
this.attachRipple(host, ripple);
180-
return ripple;
181-
}
165+
const isNoopAnimations = this._animationMode === 'NoopAnimations';
166+
const globalOptions = this._globalRippleOptions;
167+
const enterDuration = isNoopAnimations
168+
? 0
169+
: globalOptions?.animation?.enterDuration ?? defaultRippleAnimationConfig.enterDuration;
170+
const exitDuration = isNoopAnimations
171+
? 0
172+
: globalOptions?.animation?.exitDuration ?? defaultRippleAnimationConfig.exitDuration;
173+
const target: RippleTarget = {
174+
rippleDisabled:
175+
isNoopAnimations || globalOptions?.disabled || host.hasAttribute(matRippleDisabled),
176+
rippleConfig: {
177+
centered: host.hasAttribute(matRippleCentered),
178+
terminateOnPointerUp: globalOptions?.terminateOnPointerUp,
179+
animation: {
180+
enterDuration,
181+
exitDuration,
182+
},
183+
},
184+
};
185+
186+
const renderer = new RippleRenderer(target, this._ngZone, rippleEl, this._platform);
187+
const hasSetUpEvents = !target.rippleDisabled;
188+
189+
if (hasSetUpEvents) {
190+
renderer.setupTriggerEvents(host);
191+
}
192+
193+
this._hosts.set(host, {
194+
target,
195+
renderer,
196+
hasSetUpEvents,
197+
});
182198

183-
attachRipple(host: HTMLElement, ripple: MatRipple): void {
184199
host.removeAttribute(matRippleUninitialized);
185-
this._hosts.set(host, ripple);
186200
}
187201

188-
destroyRipple(host: HTMLElement) {
202+
destroyRipple(host: HTMLElement): void {
189203
const ripple = this._hosts.get(host);
190204

191205
if (ripple) {
192-
// Since this directive is created manually, it needs to be destroyed manually too.
193-
// tslint:disable-next-line:no-lifecycle-invocation
194-
ripple.ngOnDestroy();
206+
ripple.renderer._removeTriggerEvents();
195207
this._hosts.delete(host);
196208
}
197209
}

tools/public_api_guard/material/core.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,6 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget {
402402
// @public
403403
export class MatRippleLoader implements OnDestroy {
404404
constructor();
405-
// (undocumented)
406-
attachRipple(host: HTMLElement, ripple: MatRipple): void;
407405
configureRipple(host: HTMLElement, config: {
408406
className?: string;
409407
centered?: boolean;

0 commit comments

Comments
 (0)