不确定模板,但可以在一些解决方法的帮助下通过内容投影动态注入 svg-s。
主要的解决方法是找到一种方法来删除组件的宿主元素,这可以通过创建如下指令来处理,该指令将处理某些元素的“展开”。如下所示,该指令执行类似于 js 中的展开运算符的操作,它获取其所附加的元素的所有子元素并将它们展开到父元素的级别
@Directive({
selector: '[appRemoveHost]',
standalone: true,
})
export class RemoveHostDirective {
constructor(private el: ElementRef) {}
//wait for the component to render completely
ngOnInit() {
var nativeElement: HTMLElement = this.el.nativeElement,
parentElement: HTMLElement = nativeElement.parentElement;
// move all children out of the element
while (nativeElement.firstChild) {
parentElement.insertBefore(nativeElement.firstChild, nativeElement);
}
// remove the empty element(the host)
parentElement.removeChild(nativeElement);
}
}
感谢这个问题和答案删除由角度组件创建的主机 HTML 元素选择器
之后,您只需为要使用的 SVG 元素创建包装器组件即可。
示例画布组件看起来像这样
@Component({
selector: 'app-canvas',
template: `<svg width="400" height="400">
<ng-content></ng-content>
</svg>`,
standalone: true,
imports: [CommonModule],
})
export class CanvasComponent {}
自定义 SVG 元素可以如下所示
@Component({
selector: 'app-custom-rect,[app-custom-rect]',
template: `<svg appRemoveHost>
<rect [attr.x]="x" [attr.y]="y" [attr.width]="width" [attr.height]="height" style="{{ style }}" />
</svg>`,
standalone: true,
imports: [CommonModule, RemoveHostDirective],
})
export class CustomRectComponent {
@Input() style = '';
@Input() x = '';
@Input() y = '';
@Input() width = '';
@Input() height = '';
}
这里值得一提的是,否则我们需要创建
svg
包装元素
Only void and foreign elements can be self-closed "rect"
作为 rect 元素只能存在于 svg-s 中。
正如此处所示,我们正在使用 appRemoveHost,这要归功于当我们的组件被渲染时, 包装器将被删除。
最后,在我们想要使用画布的地方,我们应该执行以下操作
<app-canvas>
<app-custom-rect
appRemoveHost
[y]="'50'"
[x]="'0'"
[width]="'100'"
[height]="'100'"
[style]="'fill:red'"
></app-custom-rect>
<app-canvas/>
这里我们再次需要使用 appRemoveHost 指令来删除 rect 组件的剩余包装。
在这里您可以找到工作 Stackblitz
是否可以注入作为子 svg 元素的动态组件?
是的,当然。 Angular 已经支持这一点好几年了,但我不确定我是否已经看到了这一点的详细记录。
当你创建 Angular 组件时,你的选择器可以是很多东西。它不一定是一个标签。我们将利用这一点来使用属性。
首先,让我们构建托管 SVG 的主要组件:
import { Component } from '@angular/core';
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css'],
})
export class MainComponent {}
和 HTML:
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<svg:g house></svg:g>
</svg>
正如您可能已经猜到的,诀窍就在这里:
<svg:g house></svg:g>
。我们创建一个组,它不会渲染任何内容。但我们给它添加了一个属性house
。
然后要在那里“注入”组件,我们可以让组件选择器以带有组的 svg 标签和
house
属性为目标:
import { Component } from '@angular/core';
@Component({
selector: 'svg:g[house]',
templateUrl: './house.component.html',
styleUrls: ['./house.component.css'],
})
export class HouseComponent {}
最后一块拼图几乎就是 SVG:要注入的部分
<svg:polygon points="25,10 40,30 10,30" style="fill:rgb(0,0,255);" />
<svg:rect width="30" height="30" x="10" y="30" style="fill:rgb(0,0,255);" />
如果我们查看渲染的 HTML:
<app-main _ngcontent-rcx-c227="" _nghost-rcx-c226=""
><svg
_ngcontent-rcx-c226=""
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<g _ngcontent-rcx-c226="" house="" _nghost-rcx-c217="">
<polygon
_ngcontent-rcx-c217=""
points="25,10 40,30 10,30"
style="fill: rgb(0,0,255);"
></polygon>
<rect
_ngcontent-rcx-c217=""
width="30"
height="30"
x="10"
y="30"
style="fill: rgb(0,0,255);"
></rect>
</g></svg
></app-main>
没有我们内部组件的任何痕迹。当然,您可以根据需要拥有任意数量和嵌套的数量。您还可以通过将数据作为输入传递来参数化它们,就像使用任何其他组件或注入服务一样。
这是 Stackblitz 上的 现场演示。
jscw 使用本机 JavaScript Web 组件可能会更容易
<svg-floorplan>
处理其innerHTML<SVG>
<room>
(打造出色的语义 HTML)x,y,width,height,fill
属性 <rect>
<rect>
标签可以定位在中间<SVG>
<svg-floorplan viewBox="0 0 300 100">
<style>text{fill:gold}</style>
<room rect="0 0 200 50">living</room>
<room rect="200 0 100 100 blue">kitchen</room>
<room rect="0 50 100 50">bedroom</room>
<room rect="100 50 100 50 green">bathroom</room>
<circle fill="red" cx="25" cy="25" r="10"></circle>
</svg-floorplan>
创造:
<svg-floorplan viewBox="0 0 300 100">
<style>text{fill:gold}</style>
<room rect="0 0 200 50">living</room>
<room rect="200 0 100 100 blue">kitchen</room>
<room rect="0 50 100 50">bedroom</room>
<room rect="100 50 100 50 green">bathroom</room>
<circle fill="red" cx="25" cy="25" r="10"></circle>
</svg-floorplan>
<script>
customElements.define("svg-floorplan", class extends HTMLElement {
connectedCallback() {
setTimeout(() => { // make sure innerHTML is parsed
let svg = `<svg viewBox="${this.getAttribute("viewBox")}">`;
svg += `<rect x="0" y="0" width="100%" height="100%" fill="grey"/>`;
this.querySelectorAll('room[rect]').forEach(rm => {
let [x, y, width, height, fill="grey"] = rm.getAttribute("rect").split(" ");
svg += `<rect x="${x}" y="${y}" width="${width}" height="${height}" stroke="black" fill="${fill}"/>`;
let label = rm.innerHTML;
svg += `<path id="${label}" pathLength="100" d="M${x} ${~~y+height/2}h${width}" stroke="none"></path>
<text><textPath href="#${label}" startoffset="50"
dominant-baseline="middle" text-anchor="middle">${label}</textPath></text>`;
rm.remove();
});
svg += `</svg>`;
this.attachShadow({mode:"open"}).innerHTML = svg;
this.shadowRoot.querySelector("svg")
.insertAdjacentHTML("beforeend",this.innerHTML)
})
}
})
</script>
文档可能会帮助您:
您可以在 Angular 应用程序中使用 SVG 文件作为模板。当您使用 SVG 作为模板时,您可以像使用 HTML 模板一样使用指令和绑定。使用这些功能动态生成交互式图形。
我建议你创建一个可以以 Angular 方式控制的 SVG 组件,对于动态模板显示,你可以做的是:
<div class="container" *appCanvasAnchor>
</div>
.container {
width: 400px;
height: 400px;
svg {
width: 100%;
height: 100%;
}
}