Is it possible to inject dynamic components that are child svg elements?
Yes, definitely. Angular has been supporting that for a few years already, but I'm not sure I've seen this very well documented.
When you create an Angular component, your selector can be a lot of things. It doesn't have to be a tag necessarily. And we're going to take advantage of that to use an attribute instead.
First, lets build our main component that'll host the SVG:
import { Component } from '@angular/core';
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css'],
})
export class MainComponent {}
And the HTML:
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<svg:g house></svg:g>
</svg>
As you probably guessed already, the trick is here: <svg:g house></svg:g>
. We create a group, that won't render anything. But we add an attribute house
to it.
And then to "inject" a component there, we can have a component selector target an svg tag with a group, and a house
attribute:
import { Component } from '@angular/core';
@Component({
selector: 'svg:g[house]',
templateUrl: './house.component.html',
styleUrls: ['./house.component.css'],
})
export class HouseComponent {}
And final piece of the puzzle that's pretty much just SVG: The one to inject
<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);" />
If we look at the rendered 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>
Not a single trace of our inner component. Of course, you can have as many and as nested as you wish. You can also parameterize them by passing data as input like you'd with any other component or inject services.
Here's a live demo on Stackblitz.