我使用 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 属性的值。我还不知道这是否也有效,但我很担心。有人知道我该如何解决这个问题吗?
这两个url在构建项目后将不起作用。
'../../../../assets/utility-styles.css',
'../../../../assets/color-defs.css',
因为资产相对于已部署应用程序的根 URL 具有不同的路径。你应该使用:
'assets/utility-styles.css',
'assets/color-defs.css',