动态创建角度 svg 组件

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

这个问题专门针对 SVG 组件,即使用 svg 模板的组件。我知道如何在 Angular 18 中动态创建组件,这个问题比这个更具体,因为它是关于动态创建 svg 组件。

我已经创建了必要的 github 存储库来支持这个问题,可以在here

找到它

那么进入正题,这可能更多的是关于如何在动态创建的组件中使用属性选择器,但我不想引导见证人。

期望的结果是能够拥有某种容纳

<svg>
元素的容器组件,并能够动态添加容纳 svg 元素的特定组件。

所以在这个例子中

export class DrawingSurfaceComponent {
  @ViewChild('svgRef', { read: ViewContainerRef })
  svgRef!: ViewContainerRef;
  registry:any[] = [];
  registerDrawingElementType(drawingElementType:any){
    this.registry.push(drawingElementType);
  }
  drawElement(drawingElementName:string){
    const [ el ] = this.registry.filter(r => r.name = drawingElementName);
    const newComponentRef = this.svgRef.createComponent(el.elementType);
  }
}

使用以下模板

<svg>
    <g #svgRef></g>
</svg>

我想要的是 drawElement 方法动态添加我的矩形组件,从而产生以下输出

<svg>
  <g>
    <rect ... >
  </g>
</svg>

相反我得到的是

<svg>
  <g>
    <app-rectangle _nghost-ng-c578836850=""><rect _ngcontent-ng-c578836850="" width="200" height="100" x="10" y="10" rx="20" ry="20" fill="blue"></rect></app-rectangle>
  </g>
</svg>

鉴于 app-rectangle 不是有效的 svg,因此不会绘制矩形。

如果我尝试使用像这样的属性选择器

@Component({
  selector: '[my-rect]',
  standalone: true,
  imports: [],
  templateUrl: './rectangle.component.html',
  styleUrl: './rectangle.component.css',
  schemas: [
    NO_ERRORS_SCHEMA
  ]
})
export class RectangleComponent {

}

使用以下模板

<rect my-rect width="200" height="100" x="10" y="10" rx="20" ry="20" fill="blue" />

我根本没有任何输出

<svg _ngcontent-ng-c3197269335=""><g _ngcontent-ng-c3197269335=""></g><!--container--></svg>

那么我如何在 Angular 18 中实现这一目标?

angular svg dynamic components
1个回答
0
投票

奇怪的是,假设你有这个 HTML:

<svg>
  <g #svgRef></g>
</svg>

和跑步

this.svgRef.createComponent(el.elementType);

你得到:

<svg>
  <g>
    <app-rectangle ...></rect></app-rectangle>
  </g>
</svg>

app-rectangle
组件应添加为
<g>
的兄弟组件,而不是其子组件:

<svg>
  <g></g>
  <app-rectangle ...></rect></app-rectangle>
</svg>

所以,我会将模板更改为:

<svg>
  <g>
    <ng-container #svgRef></ng-container>
  </g>
</svg>

我建议做的是用

<rect ... />
包装部分 SVG 代码(在示例中为
<ng-template>
)并通过 public
TemplateRef
公开它,例如:

@Component({
  standalone: true,
  template: `
    <ng-template>
      <rect width="200" height="100" x="10" y="10" rx="20" ry="20" fill="blue" />
    <ng-template>  
  `,
  schemas: [NO_ERRORS_SCHEMA]
})
export class Rect {
  @ViewChild(TemplateRef, { static: true }) rectRef!: TemplateRef<unknown>;
}

然后使用它:

drawElement(drawingElementName:string){
  // clear view container
  this.svgRef.clear(); 
  const [ el ] = this.registry.filter(r => r.name = drawingElementName);
  // embed component
  const newComponentRef = this.svgRef.createComponent(el.elementType, { index: 0 });
  // embed template ref exposed by created component
  this.svgRef.createEmbeddedView(newComponentRef.instance.rectRef, { index: 1 });
  // remove component (we needed only its template ref)
  this.svgRef.remove(0);
}

我创建了STACKBLITZ来玩

© www.soinside.com 2019 - 2024. All rights reserved.