I thought I would post this example to show how I migrated some older code to Angular 13 that replaced HTML elements with Angular Components. My code originally used ComponentFactoryResolver which is now deprecated. If there is a better solution out there, I would be happy to update this!
Context: My use case was to add some customization to the display of parsed markdown by extending ngx-markdown. I picked directives as it fit well and was easy enough to implement. Specifically I was wanting to use Angular components to customize code blocks, images, and videos in a web app. This example shows what I did to replace video elements with my own, custom video player.
Original Directive
Here's the original way that I implemented my directive this using the ComponentFactoryResolver with pre-Angular 13.
import { DOCUMENT } from '@angular/common';
import { ApplicationRef, ComponentFactoryResolver, Directive, ElementRef, HostListener, Inject, Injector} from '@angular/core';
import { VideoPlayerComponent } from '../components/video-player/video-player.component';
@Directive({
selector: 'markdown,[markdown]'
})
export class VideoReplacementDirective {
constructor(
@Inject(DOCUMENT) private document: Document,
private injector: Injector,
private applicationRef: ApplicationRef,
private componentFactoryResolver: ComponentFactoryResolver,
private element: ElementRef<HTMLElement>
) {}
@HostListener('ready')
public processVideos() {
// find our videos to replace
const els = this.element.nativeElement.querySelectorAll<HTMLVideoElement>('video');
// process each element
for (let i = 0; i < els.length; i++) {
const v = els[i];
// get the parent of our video and make sure it exists to make TS happy below
const parent = v.parentElement;
if (parent) {
// create container element
const container = this.document.createElement('div');
// make our component
const component = this.componentFactoryResolver
.resolveComponentFactory(VideoPlayerComponent)
.create(this.injector, [], container);
this.applicationRef.attachView(component.hostView);
// set the source for the video
component.instance.src = v.getElementsByTagName('source')[0].src;
// replace our element
parent.replaceChild(container, v);
}
}
}
}