示例:Angular 13指令用Angular组件替换HTML元素



我想发布这个例子来展示我是如何将一些旧代码迁移到Angular 13的,该代码用Angular Components替换了HTML元素。我的代码最初使用ComponentFactoryResolver,现在已弃用。如果有更好的解决方案,我很乐意更新!

上下文:我的用例是通过扩展ngx-markdown来为解析后的markdown的显示添加一些自定义。我选择了指令,因为它非常适合并且很容易实现。特别是,我想使用Angular组件在web应用程序中自定义代码块、图像和视频。这个例子展示了我如何用自己的自定义视频播放器替换视频元素。

原始指令

这是我使用带有pre-Angular 13的ComponentFactoryResolver实现指令的原始方式。

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);
}
}
}
}

Angular 13的更新指令

对于Angular 13,文档和不推荐使用的注释提到您应该使用ViewContainerRef,但没有告诉您所有的细节来复制我以前所做的事情。

本质I:

  1. 使用ViewContainerRef制作Angular组件
  2. 更新我的Angular组件,使其ElementRef成为公共属性
  3. 用新建Angular组件的elementRef替换现有的HTMLElement

这是更新的指令代码,对我来说更容易理解和遵循:

import { Directive, ElementRef, HostListener, ViewContainerRef } from '@angular/core';
import { VideoPlayerComponent } from '../components/video-player/video-player.component';
@Directive({
selector: 'markdown,[markdown]'
})
export class VideoReplacementDirective {
constructor(private view: ViewContainerRef, 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) {
// make our component
const component = this.view.createComponent(VideoPlayerComponent);
// set the source for the new video
component.instance.src = v.getElementsByTagName('source')[0].src;
// replace our element
parent.replaceChild(component.instance.elementRef.nativeElement, v);
}
}
}
}

这里是我更新的组件,在构造函数中添加了ElementRef来显示它。

import { Component, ElementRef, Input } from '@angular/core';
@Component({
selector: 'vis-ngx-markdown-video-player',
templateUrl: './video-player.component.html',
styleUrls: ['./video-player.component.scss']
})
export class VideoPlayerComponent {
/** URL for the video to play */
@Input() public src!: string;
// add elementRef as public property
constructor(public elementRef: ElementRef<HTMLElement>) {}
}

最新更新