diff --git a/src/material/chips/chip-option.html b/src/material/chips/chip-option.html
index 5970e0328e51..43f81bf23db3 100644
--- a/src/material/chips/chip-option.html
+++ b/src/material/chips/chip-option.html
@@ -1,7 +1,3 @@
-
diff --git a/src/material/chips/chip-row.html b/src/material/chips/chip-row.html
index 83c8607c2450..63157ab2919d 100644
--- a/src/material/chips/chip-row.html
+++ b/src/material/chips/chip-row.html
@@ -1,12 +1,7 @@
-
-
diff --git a/src/material/chips/chip.scss b/src/material/chips/chip.scss
index fa620a8c69dc..44a9d28cdfac 100644
--- a/src/material/chips/chip.scss
+++ b/src/material/chips/chip.scss
@@ -184,7 +184,7 @@
}
// The ripple container should match the bounds of the entire chip.
-.mat-ripple.mat-mdc-chip-ripple {
+.mat-mdc-chip .mat-ripple.mat-mdc-chip-ripple {
@include layout-common.fill;
// Disable pointer events for the ripple container and state overlay because the container
diff --git a/src/material/chips/chip.spec.ts b/src/material/chips/chip.spec.ts
index 37b2127708f1..a5f828a20e61 100644
--- a/src/material/chips/chip.spec.ts
+++ b/src/material/chips/chip.spec.ts
@@ -1,7 +1,6 @@
import {Directionality} from '@angular/cdk/bidi';
import {Component, DebugElement, ViewChild} from '@angular/core';
import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing';
-import {MatRipple} from '@angular/material/core';
import {By} from '@angular/platform-browser';
import {Subject} from 'rxjs';
import {MatChip, MatChipEvent, MatChipSet, MatChipsModule} from './index';
@@ -11,8 +10,6 @@ describe('MDC-based MatChip', () => {
let chipDebugElement: DebugElement;
let chipNativeElement: HTMLElement;
let chipInstance: MatChip;
- let chipRippleDebugElement: DebugElement;
- let chipRippleInstance: MatRipple;
let dir = 'ltr';
@@ -74,9 +71,8 @@ describe('MDC-based MatChip', () => {
fixture = TestBed.createComponent(BasicChip);
fixture.detectChanges();
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
- chipRippleDebugElement = chipDebugElement.query(By.directive(MatRipple))!;
- chipRippleInstance = chipRippleDebugElement.injector.get(MatRipple);
- expect(chipRippleInstance.disabled)
+ chipInstance = chipDebugElement.injector.get(MatChip);
+ expect(chipInstance.ripple.disabled)
.withContext('Expected basic chip ripples to be disabled.')
.toBe(true);
});
@@ -93,8 +89,6 @@ describe('MDC-based MatChip', () => {
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
chipNativeElement = chipDebugElement.nativeElement;
chipInstance = chipDebugElement.injector.get(MatChip);
- chipRippleDebugElement = chipDebugElement.query(By.directive(MatRipple))!;
- chipRippleInstance = chipRippleDebugElement.injector.get(MatRipple);
testComponent = fixture.debugElement.componentInstance;
primaryAction = chipNativeElement.querySelector('.mdc-evolution-chip__action--primary')!;
});
@@ -137,27 +131,27 @@ describe('MDC-based MatChip', () => {
});
it('should be able to disable ripples with the `[rippleDisabled]` input', () => {
- expect(chipRippleInstance.disabled)
+ expect(chipInstance.ripple.disabled)
.withContext('Expected chip ripples to be enabled.')
.toBe(false);
testComponent.rippleDisabled = true;
fixture.detectChanges();
- expect(chipRippleInstance.disabled)
+ expect(chipInstance.ripple.disabled)
.withContext('Expected chip ripples to be disabled.')
.toBe(true);
});
it('should disable ripples when the chip is disabled', () => {
- expect(chipRippleInstance.disabled)
+ expect(chipInstance.ripple.disabled)
.withContext('Expected chip ripples to be enabled.')
.toBe(false);
testComponent.disabled = true;
fixture.detectChanges();
- expect(chipRippleInstance.disabled)
+ expect(chipInstance.ripple.disabled)
.withContext('Expected chip ripples to be disabled.')
.toBe(true);
});
diff --git a/src/material/chips/chip.ts b/src/material/chips/chip.ts
index 98cf5b249441..9e7038e93bf6 100644
--- a/src/material/chips/chip.ts
+++ b/src/material/chips/chip.ts
@@ -29,6 +29,8 @@ import {
ContentChildren,
QueryList,
OnInit,
+ DoCheck,
+ inject,
} from '@angular/core';
import {DOCUMENT} from '@angular/common';
import {
@@ -43,6 +45,7 @@ import {
mixinTabIndex,
mixinDisabled,
RippleGlobalOptions,
+ MatRippleLoader,
} from '@angular/material/core';
import {FocusMonitor} from '@angular/cdk/a11y';
import {merge, Subject, Subscription} from 'rxjs';
@@ -123,14 +126,12 @@ export class MatChip
CanColor,
CanDisableRipple,
CanDisable,
+ DoCheck,
HasTabIndex,
OnDestroy
{
protected _document: Document;
- /** Whether the ripple is centered on the chip. */
- readonly _isRippleCentered = false;
-
/** Emits when the chip is focused. */
readonly _onFocus = new Subject();
@@ -251,11 +252,22 @@ export class MatChip
* @deprecated Considered an implementation detail. To be removed.
* @breaking-change 17.0.0
*/
- @ViewChild(MatRipple) ripple: MatRipple;
+ get ripple(): MatRipple {
+ return this._rippleLoader?.getRipple(this._elementRef.nativeElement)!;
+ }
+ set ripple(v: MatRipple) {
+ this._rippleLoader?.attachRipple(this._elementRef.nativeElement, v);
+ }
/** Action receiving the primary set of user interactions. */
@ViewChild(MatChipAction) primaryAction: MatChipAction;
+ /**
+ * Handles the lazy creation of the MatChip ripple.
+ * Used to improve initial load time of large applications.
+ */
+ _rippleLoader: MatRippleLoader = inject(MatRippleLoader);
+
constructor(
public _changeDetectorRef: ChangeDetectorRef,
elementRef: ElementRef,
@@ -275,6 +287,11 @@ export class MatChip
this.tabIndex = parseInt(tabIndex) ?? this.defaultTabIndex;
}
this._monitorFocus();
+
+ this._rippleLoader?.configureRipple(this._elementRef.nativeElement, {
+ className: 'mat-mdc-chip-ripple',
+ disabled: this._isRippleDisabled(),
+ });
}
ngOnInit() {
@@ -305,6 +322,10 @@ export class MatChip
).subscribe(() => this._changeDetectorRef.markForCheck());
}
+ ngDoCheck(): void {
+ this._rippleLoader.setDisabled(this._elementRef.nativeElement, this._isRippleDisabled());
+ }
+
ngOnDestroy() {
this._focusMonitor.stopMonitoring(this._elementRef);
this._actionChanges?.unsubscribe();
diff --git a/src/material/core/private/ripple-loader.ts b/src/material/core/private/ripple-loader.ts
index 41fca0c6b432..17f03e218662 100644
--- a/src/material/core/private/ripple-loader.ts
+++ b/src/material/core/private/ripple-loader.ts
@@ -75,6 +75,7 @@ export class MatRippleLoader implements OnDestroy {
config: {
className?: string;
centered?: boolean;
+ disabled?: boolean;
},
): void {
// Indicates that the ripple has not yet been rendered for this component.
@@ -89,6 +90,10 @@ export class MatRippleLoader implements OnDestroy {
if (config.centered) {
host.setAttribute(matRippleCentered, '');
}
+
+ if (config.disabled) {
+ host.setAttribute(matRippleDisabled, '');
+ }
}
/** Returns the ripple instance for the given host element. */
diff --git a/tools/public_api_guard/material/chips.md b/tools/public_api_guard/material/chips.md
index b1e574bd17ba..532ec1e2be14 100644
--- a/tools/public_api_guard/material/chips.md
+++ b/tools/public_api_guard/material/chips.md
@@ -31,6 +31,7 @@ import { InjectionToken } from '@angular/core';
import { MatFormField } from '@angular/material/form-field';
import { MatFormFieldControl } from '@angular/material/form-field';
import { MatRipple } from '@angular/material/core';
+import { MatRippleLoader } from '@angular/material/core';
import { NgControl } from '@angular/forms';
import { NgForm } from '@angular/forms';
import { NgZone } from '@angular/core';
@@ -61,7 +62,7 @@ export const MAT_CHIP_TRAILING_ICON: InjectionToken;
export const MAT_CHIPS_DEFAULT_OPTIONS: InjectionToken;
// @public
-export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, AfterContentInit, CanColor, CanDisableRipple, CanDisable, HasTabIndex, OnDestroy {
+export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, AfterContentInit, CanColor, CanDisableRipple, CanDisable, DoCheck, HasTabIndex, OnDestroy {
constructor(_changeDetectorRef: ChangeDetectorRef, elementRef: ElementRef, _ngZone: NgZone, _focusMonitor: FocusMonitor, _document: any, animationMode?: string, _globalRippleOptions?: RippleGlobalOptions | undefined, tabIndex?: string);
protected _allLeadingIcons: QueryList;
protected _allRemoveIcons: QueryList;
@@ -90,7 +91,6 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit,
protected _highlighted: boolean;
id: string;
_isBasicChip: boolean;
- readonly _isRippleCentered = false;
_isRippleDisabled(): boolean;
leadingIcon: MatChipAvatar;
// (undocumented)
@@ -98,6 +98,8 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit,
// (undocumented)
ngAfterViewInit(): void;
// (undocumented)
+ ngDoCheck(): void;
+ // (undocumented)
ngOnDestroy(): void;
// (undocumented)
ngOnInit(): void;
@@ -114,7 +116,9 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit,
readonly removed: EventEmitter;
removeIcon: MatChipRemove;
// @deprecated
- ripple: MatRipple;
+ get ripple(): MatRipple;
+ set ripple(v: MatRipple);
+ _rippleLoader: MatRippleLoader;
role: string | null;
trailingIcon: MatChipTrailingIcon;
get value(): any;
@@ -407,6 +411,8 @@ export class MatChipRow extends MatChip implements AfterViewInit {
// (undocumented)
_isEditing: boolean;
// (undocumented)
+ _isRippleDisabled(): boolean;
+ // (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
diff --git a/tools/public_api_guard/material/core.md b/tools/public_api_guard/material/core.md
index ac9be0792f67..4844f95ca2f0 100644
--- a/tools/public_api_guard/material/core.md
+++ b/tools/public_api_guard/material/core.md
@@ -403,6 +403,7 @@ export class MatRippleLoader implements OnDestroy {
configureRipple(host: HTMLElement, config: {
className?: string;
centered?: boolean;
+ disabled?: boolean;
}): void;
createRipple(host: HTMLElement): MatRipple | undefined;
getRipple(host: HTMLElement): MatRipple | undefined;