I went through this issue while working on the ScheduleJS framework. At some point I am provided with a HTMLCanvasElement
which I want to replace with a dynamically generated component programatically. To do so, and to keep the code as clean as possible, I'd like to create my own Angular components at runtime and use the HTMLCanvasElement.replaceWith(component)
method from the provided HTMLCanvasElement
replacing the canvas with the dynamically created component.
Here is the Angular service I came up with, which does the job the way I expected:
import {ApplicationRef, ComponentFactoryResolver, ComponentRef, Injectable, Injector, Type} from "@angular/core";
import {ReplacementComponent} from "xxx"; // This is a higher order type of Component
@Injectable({providedIn: "root"})
export class DynamicComponentGenerator {
// Attributes
private _components: Map<string, ComponentRef<ReplacementComponent>> = new Map();
private _currentKey: number = 0;
// Constructor
constructor(private _appRef: ApplicationRef,
private _resolver: ComponentFactoryResolver,
private _injector: Injector) { }
// Methods
create(componentType: Type<ReplacementComponent>): ComponentRef<ReplacementComponent> {
const componentRef = componentType instanceof ComponentRef
? componentType
: this._resolver.resolveComponentFactory(componentType).create(this._injector);
this._appRef.attachView(componentRef.hostView);
this._components.set(`${this._currentKey}`, componentRef);
componentRef.instance.key = `${this._currentKey}`;
this._currentKey = 1;
return componentRef;
}
remove(componentKey: string): void {
const componentRef = this._components.get(componentKey);
if (componentRef) {
this._appRef.detachView(componentRef.hostView);
componentRef.destroy();
this._components.delete(componentKey);
}
}
clear(): void {
this._components.forEach((componentRef, key) => {
this._appRef.detachView(componentRef.hostView);
componentRef.destroy();
this._components.delete(key);
});
this._currentKey = 0;
}
}
So basically this service lets me create a component with .create(ComponentClass)
remove it by providing the component key .remove(key)
and clear()
to remove all the components.
My issues are the following:
- The
ComponentFactoryResolver
class is deprecated, should I use it anyways? - Could not manage to use the newer API to create unattached components (not able to have access to an Angular hostView)
- Is there a better way to do this?
Thank you for reading me.
CodePudding user response:
You could try using new createComponent function:
import { createComponent, ... } from "@angular/core";
const componentRef =
createComponent(componentType, { environmentInjector: this._appRef.injector});
this._appRef.attachView(componentRef.hostView);