我们如何让外部样式表加载到shadow DOM中?

问题描述 投票:0回答:1

我使用 Angular 16 为我为响应式设计创建的新 CSS 实用程序构建了一个文档网站。为了演示响应式功能,我创建了一个具有可调整大小的

<iframe>
的组件,并将演示组件嵌入其中。我这样做是为了利用
<iframe>
的单独 DOM 实例,以便演示响应
<iframe>
的大小,而不必调整整个浏览器窗口的大小,这对用户来说很方便。

我需要使用两个样式表,一个用于我的 CSS 实用程序,另一个用于我的所有颜色定义。我能够让它们工作的唯一方法是将它们导入到我的角度项目的主

styles.css
并将其附加到
<iframe>
的头部。

我的组件的代码看起来像这样,减去了一些与问题无关的内容。

@Component({
  selector        : 'responsive-window',
  standalone      : true,
  imports         : [ CommonModule ],
  templateUrl     : './responsive-window.component.html',
  styleUrls       : ['./responsive-window.component.css'],
  changeDetection : ChangeDetectionStrategy.OnPush
})

export class ResponsiveWindowComponent implements OnInit, AfterViewInit, AfterContentChecked, OnDestroy {
    
    /* I pass the components I want to embed through this Input */
    @Input() Demonstration! : Type<Component>;

    /* I use this to target the iframe in the template */
    @ViewChild('domFrame', {static: true, read: ElementRef}) DomFrame! : ElementRef;

    constructor(
        private cdr   : ChangeDetectorRef,
        private vcRef : ViewContainerRef,
        private zone  : NgZone
    ){}

    ngAfterViewInit(): void {

        /* embeds the component into the iframe after the view is loaded */
        this.embedContent();

    }

    public embedContent(): void{
      /* targets the iframe */
      const frame = this.DomFrame?.nativeElement.contentDocument || this.DomFrame?.nativeElement.contentWindow;

      /* resolves the component */
      const item : ComponentRef<any> = this.vcRef.createComponent<Component>(this.Demonstration);

      /* for setting up a broadcast channel to send data back and forth from outside the iframe */
      if(reflectComponentType(this.Demonstration)?.inputs.find(a=> a.propName === 'BroadcastName') !== undefined){
        item.instance.BroadcastName = this.BroadcastName;
        item.instance.ChannelName   = this.ChannelName;
        item.instance.TargetName    = this.TargetName;
      }

      /* for the default styling of the iframe's body element */
      const defaultStyles = document.createElement('style');
     
      /* for attaching the main stylesheet of the angular app */
      const stylesLink     = document.createElement('link');

      defaultStyles.innerText = `*{ padding : 0; margin : 0; box-sizing: border-box; overflow: hidden; } body{ display : grid; place-items: center; min-height: 0px; max-height: 100vh; grid-template-columns: 1fr; grid-template-rows: 1fr; background-color: hsl(var(--gray-100), 1); }`;
    
      stylesLink.rel = 'stylesheet';
      stylesLink.type = 'text/css';
      stylesLink.href = 'styles.css';

      /* embedding everything into the iframe */
      frame.head.appendChild(defaultStyles);
      frame.head.appendChild(stylesLink);
      frame.body.appendChild(item.location.nativeElement);
    }
}

就我传递到此组件以嵌入到

<iframe>
中的组件而言,这是它们如何设置的示例。

@Component({
  selector      : 'app-example',
  standalone    : true,
  imports       : [CommonModule],
  templateUrl   : './example.component.html',
  styleUrls     : [ './example.component.css' ],
  encapsulation : ViewEncapsulation.ShadowDom
})
export class ExampleComponent {

}

我必须将封装设置为

ShadowDom
,否则组件的任何样式都不会生效,也不会从嵌入到
styles.css
中的
<head>
文件中读取任何样式。运行
ng serve
时一切正常,但是当我运行
ng-build
并将文件加载到我的
c-panel
并访问该站点时,
styles.css
文件中的任何样式都不会对嵌入到
 中的组件生效<iframe>

我更改了嵌入组件的样式表中的一些样式,只是为了看看它们是否会生效,它们确实做到了,这意味着它可以识别组件样式表中的样式,但是当查看来自我的样式表的一些定义时,其中包含所有颜色和我的 CSS 实用程序,它们是 CSS 变量,它们都没有定义。

我尝试将这些样式表导入到我的组件的样式表中,但这不起作用。然后我尝试将它们导入到元数据中,如下所示。

@Component({
  selector: 'app-product',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './product.component.html',
  styleUrls: [
    '../../../../assets/utility-styles.css',
    '../../../../assets/color-defs.css',
    './product.component.css'
],
  encapsulation: ViewEncapsulation.ShadowDom
})

我确保先导入其他样式表,然后再导入组件的样式表,以便所有值在使用之前都已定义。我再次运行

ng build
并将文件加载到我的
c-panel
中,但这也不起作用。我还将这些额外的样式表添加到我的
styles
文件中的
angular.json
数组中,看看是否允许我将它们导入到嵌入式组件中,但也无法正常工作。

responsive-window
组件中,您会注意到我的代码中有一部分引用了
BroadcastName
ChannelName
TargetName
输入,这些输入用于设置广播频道,以便用户可以尝试不同的设置将从
<iframe>
外部的仪表板更改嵌入组件中某些 CSS 属性的值。我还不知道这是否也有效,但我很担心。有人知道我该如何解决这个问题吗?

angular shadow-dom
1个回答
0
投票

这两个url在构建项目后将不起作用。

'../../../../assets/utility-styles.css',
'../../../../assets/color-defs.css',

因为资产相对于已部署应用程序的根 URL 具有不同的路径。你应该使用:

'assets/utility-styles.css',
'assets/color-defs.css',
© www.soinside.com 2019 - 2024. All rights reserved.