From 5f132c5fe61b1548ef5f67593f3429c265964d95 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Sat, 2 Nov 2024 19:54:28 -0400 Subject: [PATCH] fix(platform-browser): correctly add external stylesheets to ShadowDOM components Angular components that use ShadowDOM view encapsulation have an alternate execution path for adding component styles to the DOM that does not use the SharedStylesHost that all other view encapsulation modes leverage. To ensure that ShadowDOM components receive all defined styles, additional logic has been added to the ShadowDOM specific renderer to also cover external styles. --- .../features/external_styles_feature.ts | 9 +++++++-- .../platform-browser/src/dom/dom_renderer.ts | 19 ++++++++++++++++++- .../src/dom/shared_styles_host.ts | 2 +- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/core/src/render3/features/external_styles_feature.ts b/packages/core/src/render3/features/external_styles_feature.ts index c31555780adf..0df3191e903a 100644 --- a/packages/core/src/render3/features/external_styles_feature.ts +++ b/packages/core/src/render3/features/external_styles_feature.ts @@ -25,10 +25,15 @@ export function ɵɵExternalStylesFeature(styleUrls: string[]): ComponentDefFeat } definition.getExternalStyles = (encapsulationId) => { - // Add encapsulation ID search parameter `component` to support external style encapsulation + // Add encapsulation ID search parameter `ngcomp` to support external style encapsulation as well as the encapsulation mode + // for usage tracking. const urls = styleUrls.map( (value) => - value + '?ngcomp' + (encapsulationId ? '=' + encodeURIComponent(encapsulationId) : ''), + value + + '?ngcomp' + + (encapsulationId ? '=' + encodeURIComponent(encapsulationId) : '') + + '&e=' + + definition.encapsulation, ); return urls; diff --git a/packages/platform-browser/src/dom/dom_renderer.ts b/packages/platform-browser/src/dom/dom_renderer.ts index 1bfc775ce6df..63891746c3f2 100644 --- a/packages/platform-browser/src/dom/dom_renderer.ts +++ b/packages/platform-browser/src/dom/dom_renderer.ts @@ -27,7 +27,7 @@ import { import {RuntimeErrorCode} from '../errors'; import {EventManager} from './events/event_manager'; -import {SharedStylesHost} from './shared_styles_host'; +import {createLinkElement, SharedStylesHost} from './shared_styles_host'; export const NAMESPACE_URIS: {[ns: string]: string} = { 'svg': 'http://www.w3.org/2000/svg', @@ -434,6 +434,23 @@ class ShadowDomRenderer extends DefaultDomRenderer2 { styleEl.textContent = style; this.shadowRoot.appendChild(styleEl); } + + // Apply any external component styles to the shadow root for the component's element. + // The ShadowDOM renderer uses an alternative execution path for component styles that + // does not use the SharedStylesHost that other encapsulation modes leverage. Much like + // the manual addition of embedded styles directly above, any external stylesheets + // must be manually added here to ensure ShadowDOM components are correctly styled. + // TODO: Consider reworking the DOM Renderers to consolidate style handling. + const styleUrls = component.getExternalStyles?.(); + if (styleUrls) { + for (const styleUrl of styleUrls) { + const linkEl = createLinkElement(styleUrl, doc); + if (nonce) { + linkEl.setAttribute('nonce', nonce); + } + this.shadowRoot.appendChild(linkEl); + } + } } private nodeOrShadowRoot(node: any): any { diff --git a/packages/platform-browser/src/dom/shared_styles_host.ts b/packages/platform-browser/src/dom/shared_styles_host.ts index ec6e40642684..b34ed4c0da4c 100644 --- a/packages/platform-browser/src/dom/shared_styles_host.ts +++ b/packages/platform-browser/src/dom/shared_styles_host.ts @@ -84,7 +84,7 @@ function addServerStyles( * @param doc A DOM Document to use to create the element. * @returns An HTMLLinkElement instance. */ -function createLinkElement(url: string, doc: Document): HTMLLinkElement { +export function createLinkElement(url: string, doc: Document): HTMLLinkElement { const linkElement = doc.createElement('link'); linkElement.setAttribute('rel', 'stylesheet'); linkElement.setAttribute('href', url);