I found the solution. Actually I found an excellent post on github
https://github.com/shivs25/angular5-canvas-drawer. I took this solution to implement my own.
So all the credits go to Billy Shivers. Well done.
Here is the solution
The settings for line and circle can be dynamic set, below is just an example of a line and circle
CircleComponent and HTML template
import { Component } from '@angular/core';
import { ShapeComponent } from '../shape/shape.component';
@Component({
selector: 'app-circle',
templateUrl: './circle.component.html',
styleUrls: ['./circle.component.css']
})
export class CircleComponent extends ShapeComponent {
constructor() {
super('circle');
}
}
html
<ng-template #elementTemplate>
<svg:circle [attr.cx]="50" [attr.cy]="50" [attr.r]="40" stroke="black" stroke-width="3" fill="red" />
</ng-template>>
LineComponent and HTML template
import { Component } from '@angular/core';
import { ShapeComponent } from '../shape/shape.component';
@Component({
selector: 'app-line',
templateUrl: './line.component.html',
styleUrls: ['./line.component.css']
})
export class LineComponent extends ShapeComponent {
constructor() {
super('line');
console.log('linecomponent:constructor');
}
}
html
<ng-template #elementTemplate>
<svg:line [attr.x1]="100" [attr.y1]="100" [attr.x2]="200" [attr.y2]="200" style="stroke:#006600; stroke-width:1px" />
</ng-template>>
The ShapeComponent and HTML
import { Component, OnInit, ViewChild, TemplateRef, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-shape',
templateUrl: './shape.component.html',
styleUrls: ['./shape.component.css']
})
export class ShapeComponent implements OnInit, AfterViewInit {
shapeType: string;
visible: boolean = true;
id: string = 'unknown';
@ViewChild('elementTemplate')
elementTemplate: TemplateRef<any>;
constructor(shapeType: string) {
console.log('shapecomponent constructor :', shapeType);
this.shapeType = shapeType;
}
setid(value: string): void {
this.id = value;
}
ngOnInit() {
console.log('ShapeComponent ngOnInit()');
}
ngAfterViewInit(): void {
console.log('!!!!!!!!! ShapeComponent ngAfterViewInit: ', this.elementTemplate);
}
}
html : none
The enum for component types
export enum ShapeTypes {
Line,
Circle,
Rectangle
}
The ShapeHolderComponent
import { Component, OnInit, ViewChild, TemplateRef, AfterViewInit } from '@angular/core';
import { ShapeComponent } from '../shape/shape.component';
import { LineComponent } from '../line/line.component';
import { CircleComponent } from '../circle/circle.component';
import { ShapeTypes } from '../model/shape-types';
@Component({
selector: 'app-shapeholder',
templateUrl: './shapeholder.component.html',
styleUrls: ['./shapeholder.component.css']
})
export class ShapeholderComponent implements OnInit, AfterViewInit {
@ViewChild('elementTemplate')
elementTemplate: TemplateRef<any>;
shapes: ShapeTypes[];
constructor() {
this.shapes = [];
this.shapes.push(ShapeTypes.Line);
this.shapes.push(ShapeTypes.Circle);
console.log('shapeholder shapes :', this.shapes);
}
ngOnInit() {
console.log('ShapeHolderComponent : ngOnInit()');
}
ngAfterViewInit(): void {
console.log('!!!!!!!!! ShapeHolder ngAfterViewInit: ', this.elementTemplate);
}
}
html, set height in width in css for the svg
<svg>
<ng-container *ngFor="let shape of shapes; let i = index">
<ng-container svg-dynamic [componentData]="shape">
</ng-container>
</ng-container>
</svg>
And the most import part of it, the directive
import { Directive, Input, ViewContainerRef, Injector, ComponentFactoryResolver } from '@angular/core';
import { ShapeComponent } from './shape/shape.component';
import { LineComponent } from './line/line.component';
import { CircleComponent } from './circle/circle.component';
import { ShapeTypes } from './model/shape-types';
@Directive({
selector: '[svg-dynamic]'
})
export class SvgDynamicDirective {
constructor(private _viewContainerRef: ViewContainerRef, private _resolver: ComponentFactoryResolver) {
}
@Input() set componentData(data: ShapeTypes) {
console.log('set componentdata : ', data);
let injector = Injector.create([], this._viewContainerRef.parentInjector);
console.log('injector:', injector);
let factory = this._resolver.resolveComponentFactory(this.buildComponent(data));
console.log('factory:', factory);
let component = factory.create(injector);
console.log('component:', component);
let c: ShapeComponent = <ShapeComponent>component.instance;
console.log('viewContainerRef:', this._viewContainerRef);
console.log('elementTemplate:', c.elementTemplate);
this._viewContainerRef.clear();
this._viewContainerRef.createEmbeddedView(c.elementTemplate);
}
private buildComponent(data: ShapeTypes): any {
switch (data) {
case ShapeTypes.Line:
return LineComponent;
case ShapeTypes.Circle:
return CircleComponent;
}
return null;
}
}
And the app.component html
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<app-shapeholder></app-shapeholder>
</div>
The app.component
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'demo1';
}
And the app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { ShapeComponent } from './shape/shape.component';
import { LineComponent } from './line/line.component';
import { ShapeholderComponent } from './shapeholder/shapeholder.component';
import { SvgDynamicDirective } from './svg-dynamic.directive';
import { CircleComponent } from './circle/circle.component';
@NgModule({
entryComponents: [
LineComponent,
ShapeComponent,
CircleComponent
],
declarations: [
AppComponent,
LineComponent,
ShapeComponent,
CircleComponent,
ShapeholderComponent,
SvgDynamicDirective,
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
And a final screen shot of my app

I hope you find this answer usefull and can use it in your own app. The idea is to create dynamic templates views