@@ -17,18 +17,23 @@ import {
1717 OnDestroy ,
1818 inject ,
1919 DOCUMENT ,
20+ SecurityContext ,
2021} from '@angular/core' ;
22+ import { DomSanitizer , SafeHtml } from '@angular/platform-browser' ;
2123import { Subscription } from 'rxjs' ;
2224import {
2325 AriaLivePoliteness ,
2426 LiveAnnouncerDefaultOptions ,
2527 LIVE_ANNOUNCER_ELEMENT_TOKEN ,
2628 LIVE_ANNOUNCER_DEFAULT_OPTIONS ,
2729} from './live-announcer-tokens' ;
28- import { _CdkPrivateStyleLoader , _VisuallyHiddenLoader } from '../../private' ;
30+ import { _CdkPrivateStyleLoader , _VisuallyHiddenLoader , trustedHTMLFromString } from '../../private' ;
2931
3032let uniqueIds = 0 ;
3133
34+ /** Possible types for a message that can be announced by the `LiveAnnouncer`. */
35+ export type LiveAnnouncerMessage = string | SafeHtml ;
36+
3237@Injectable ( { providedIn : 'root' } )
3338export class LiveAnnouncer implements OnDestroy {
3439 private _ngZone = inject ( NgZone ) ;
@@ -38,6 +43,7 @@ export class LiveAnnouncer implements OnDestroy {
3843
3944 private _liveElement : HTMLElement ;
4045 private _document = inject ( DOCUMENT ) ;
46+ private _sanitizer = inject ( DomSanitizer ) ;
4147 private _previousTimeout : ReturnType < typeof setTimeout > ;
4248 private _currentPromise : Promise < void > | undefined ;
4349 private _currentResolve : ( ( ) => void ) | undefined ;
@@ -54,15 +60,15 @@ export class LiveAnnouncer implements OnDestroy {
5460 * @param message Message to be announced to the screen reader.
5561 * @returns Promise that will be resolved when the message is added to the DOM.
5662 */
57- announce ( message : string ) : Promise < void > ;
63+ announce ( message : LiveAnnouncerMessage ) : Promise < void > ;
5864
5965 /**
6066 * Announces a message to screen readers.
6167 * @param message Message to be announced to the screen reader.
6268 * @param politeness The politeness of the announcer element.
6369 * @returns Promise that will be resolved when the message is added to the DOM.
6470 */
65- announce ( message : string , politeness ?: AriaLivePoliteness ) : Promise < void > ;
71+ announce ( message : LiveAnnouncerMessage , politeness ?: AriaLivePoliteness ) : Promise < void > ;
6672
6773 /**
6874 * Announces a message to screen readers.
@@ -72,7 +78,7 @@ export class LiveAnnouncer implements OnDestroy {
7278 * 100ms after `announce` has been called.
7379 * @returns Promise that will be resolved when the message is added to the DOM.
7480 */
75- announce ( message : string , duration ?: number ) : Promise < void > ;
81+ announce ( message : LiveAnnouncerMessage , duration ?: number ) : Promise < void > ;
7682
7783 /**
7884 * Announces a message to screen readers.
@@ -83,9 +89,13 @@ export class LiveAnnouncer implements OnDestroy {
8389 * 100ms after `announce` has been called.
8490 * @returns Promise that will be resolved when the message is added to the DOM.
8591 */
86- announce ( message : string , politeness ?: AriaLivePoliteness , duration ?: number ) : Promise < void > ;
92+ announce (
93+ message : LiveAnnouncerMessage ,
94+ politeness ?: AriaLivePoliteness ,
95+ duration ?: number ,
96+ ) : Promise < void > ;
8797
88- announce ( message : string , ...args : any [ ] ) : Promise < void > {
98+ announce ( message : LiveAnnouncerMessage , ...args : any [ ] ) : Promise < void > {
8999 const defaultOptions = this . _defaultOptions ;
90100 let politeness : AriaLivePoliteness | undefined ;
91101 let duration : number | undefined ;
@@ -127,7 +137,22 @@ export class LiveAnnouncer implements OnDestroy {
127137
128138 clearTimeout ( this . _previousTimeout ) ;
129139 this . _previousTimeout = setTimeout ( ( ) => {
130- this . _liveElement . textContent = message ;
140+ if ( ! message || typeof message === 'string' ) {
141+ this . _liveElement . textContent = message ;
142+ } else {
143+ const cleanMessage = this . _sanitizer . sanitize ( SecurityContext . HTML , message ) ;
144+
145+ if ( cleanMessage === null && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) {
146+ throw new Error (
147+ `The message provided to LiveAnnouncer was not trusted as safe HTML by ` +
148+ `Angular's DomSanitizer. Attempted message was "${ message } ".` ,
149+ ) ;
150+ }
151+
152+ this . _liveElement . innerHTML = trustedHTMLFromString (
153+ cleanMessage || '' ,
154+ ) as unknown as string ;
155+ }
131156
132157 if ( typeof duration === 'number' ) {
133158 this . _previousTimeout = setTimeout ( ( ) => this . clear ( ) , duration ) ;
0 commit comments