在没有宿主元素容器的情况下渲染嵌套组件 - 将 SVG 部分分成不同的组件

Posted

技术标签:

【中文标题】在没有宿主元素容器的情况下渲染嵌套组件 - 将 SVG 部分分成不同的组件【英文标题】:Render nested components without host element container - Separate SVG parts into Different components 【发布时间】:2020-10-18 20:37:08 【问题描述】:

我有一个带有 SVG 元素的组件,我试图将该 SVG 的各个部分分成不同的组件。 嵌套组件需要从父组件动态创建,这是因为我从一个 json 文件构建整个 SVG 图形,该文件确定需要添加哪些嵌套组件(SVG 嵌套形状)。

为了动态创建组件,我使用了 ComponentFactoryResolver 和 ViewContainerRef 来自@angular/core

@Component(
selector: 'skick-player',
 templateUrl: './player.component.html',
 styleUrls: ['./player.component.scss'],
)
export class PlayerComponent implements OnInit, AfterViewInit 

@ViewChild('svgContainer',  read: ViewContainerRef ) container;

constructor( private resolver: ComponentFactoryResolver) 

  performFrame() 

      ....
     //Add nested component
      const factory = this.resolver.resolveComponentFactory(
        BackDropItemComponent
      );
      const componentRef = this.container.createComponent(factory);
      componentRef.instance.imagePath = objectUrl;
    
  
);

问题在于 Angular 将嵌套组件包裹在一个 div 中:

<div _nghost-qon-c42="" skick-back-drop-item="" class="ng-star-inserted">

由于创建了特殊的元素标签,因此它不会被渲染。如果该 div 元素被移除,它会正确呈现嵌套的 SVG。

我知道我可以将嵌套组件渲染为指令,它会起作用,如下所示:

<svg>
<svg:g skick-back-drop-item />
</svg>

但在我的用例中,控制器需要从父组件动态呈现,因此这种方法对我不起作用。

也许有办法以编程方式从父组件呈现(添加嵌套控制器)作为指令(请记住嵌套组件具有@input 属性)?

是否可以有不可见的组件容器?在没有任何容器的情况下渲染嵌套组件的内容?


父模板:

<div class="patch-overlay" cdkDrag>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:svgjs="http://svgjs.dev/svgjs"  >
    <<ng-container #svgContainer></ng-container>>
</svg>
</div>

嵌套组件模板(示例):

<svg  >
<rect [attr.width]="width"  style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
</svg>

【问题讨论】:

stackblitz.com/edit/… 这次请检查这个例子 Angular 没有将程序创建的组件包装在 div 中 @hanan,谢谢,它将它包装在 容器中,这就是为什么在您的示例中没有呈现矩形的原因。我发现添加了以下 CSS 规则: hello display: contents;做这个把戏,但我不太喜欢这种方法 @D.B,如果你的组件的选择器被[]包围:selector: '[skick-back-drop-item]',你可以应用到任何html标签 【参考方案1】:

我已经尝试过你正在做的事情。我的错误包括构建专门的 FormGroup 组件(不要那样做)。最近,我使用ControlValueAccessor 构建了一个组件,它运行良好,符合我的目的。

我还没有使用ng-template 及其相关功能构建任何东西,例如ngTemplateOutlet。但是,我怀疑这可能会对您有所帮助。 ng-template 在 DOM 中被移开,这正是您要寻找的。​​p>

这是 Angular 大学的explanation。我还发现 Netanel Basel 的 info 很棒。

如果您想尝试 CVA 路线,上周我刚刚完成了一个项目的重新编码,并在这里对 SO 进行了大量研究。发现了一个涉及我想做的事情的问题,然后回过头来写了一个对我有用的new answer。看看吧,它可能会给你你需要的东西。

【讨论】:

谢谢@Andrew。 ng-template 在某些情况下从 DOM 中删除,例如在实现 if/else 场景时,但是在从控制器动态注入组件时添加了一个新的 div 容器作为包装器。所以,不幸的是,这种方法对我不起作用. :(【参考方案2】:

由于这在谷歌的第一页上显示为最新的问题,这里的解决方法似乎在所有情况下都有效:使用显示内容。

@Component(
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.sass'],
  host: 
    style: "display: contents"
  
)
export class MyComponent 

这通过告诉浏览器表现得好像宿主元素根本不存在一样起作用;如果您检查元素,它仍然是 html 的一部分,但它不会成为任何渲染/大小/位置考虑的一部分。 附言这确实比在 html 中完全不包含它有一个优势,它仍然是 CSS 的可选 html 元素

【讨论】:

以上是关于在没有宿主元素容器的情况下渲染嵌套组件 - 将 SVG 部分分成不同的组件的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有要映射的对象数组的情况下在 React.js 中循环和渲染元素?

React组件渲染

Vue渲染功能:在没有包装器的情况下将插槽包含到子组件中

如何在没有 App.vue 的情况下进行组件渲染

Angular2:用组件的模板替换宿主元素

在 ReactJS 中滚动嵌套组件