惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
罗磊的独立博客
博客园 - 聂微东
T
The Blog of Author Tim Ferriss
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
F
Fortinet All Blogs
A
About on SuperTechFans
腾讯CDC
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog RSS Feed
IT之家
IT之家
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
宝玉的分享
宝玉的分享
C
Check Point Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Vercel News
Vercel News
爱范儿
爱范儿
Microsoft Security Blog
Microsoft Security Blog
月光博客
月光博客
T
Tailwind CSS Blog
The Cloudflare Blog
Hugging Face - Blog
Hugging Face - Blog

zodream梦想开源/个人编程日记

文件解析笔记-zodream梦想开源/个人编程日记 密码本开发笔记之读写与保存-zodream梦想开源/个人编程日记 SkiaSharp 把 pixel byte[] 转成 SKBitmap-zodream梦想开源/个人编程日记 nas 使用 Docker 安装 gogs-zodream梦想开源/个人编程日记 复制 android 手机中的文件到电脑-zodream梦想开源/个人编程日记 周报:寻找优质的周刊-zodream梦想开源/个人编程日记 开发日志:对Markdown的代码块新增引用来源支持-zodream梦想开源/个人编程日记 周报:怎么写技术类的教程文章-zodream梦想开源/个人编程日记 css display:flex 布局尺寸超出问题-zodream梦想开源/个人编程日记 周报:SEO优化的思考-zodream梦想开源/个人编程日记 Edge 浏览器不适用 Edge Image Viewer 打开图片 -zodream梦想开源/个人编程日记 SEO 学习笔记(一) 内容来源-zodream梦想开源/个人编程日记 PHP 实现双因素身份认证(2FA)-zodream梦想开源/个人编程日记 WPF MVVM 获取List 多选数据-zodream梦想开源/个人编程日记 Burp Suite 抓包-zodream梦想开源/个人编程日记 使用 indexnow 注意事项-zodream梦想开源/个人编程日记 Godot 使用字体图标 例如: Iconfont、FontAwesome-zodream梦想开源/个人编程日记 angular 15 对指定页面进行访问限制-zodream梦想开源/个人编程日记 CSS 使用 column-count 实现瀑布流出现内容分割的解决办法-zodream梦想开源/个人编程日记 input 确认按键事件在手机端不生效-zodream梦想开源/个人编程日记 C# 使用socket 进行通讯-zodream梦想开源/个人编程日记 Maui开发中Windows应用开启管理员权限-zodream梦想开源/个人编程日记 Maui 中自定义控件-zodream梦想开源/个人编程日记 angular 14 使用 ng-template 实现tree 结构显示-zodream梦想开源/个人编程日记 c# 动态安装和卸载dll-zodream梦想开源/个人编程日记 慎用 CompositionTarget.Rendering-zodream梦想开源/个人编程日记 c# 重写 c++ 程序笔记:数据初始化-zodream梦想开源/个人编程日记 源码编译 aseprite-zodream梦想开源/个人编程日记 记录一下字符串分隔split各语言之间的不同-zodream梦想开源/个人编程日记 c# Gzip解码无头内容-zodream梦想开源/个人编程日记
angular 14 替换 ComponentFactoryResolver 实现动态创建组件...
zodream · 2022-06-30 · via zodream梦想开源/个人编程日记

angular 14 替换 ComponentFactoryResolver 实现动态创建组件

需求

移植弹出框到 angular 项目中。

方案

直接使用其他项目依赖

比较常用的有以下两个

  1. ngx-toastr

  2. ng-bootstrap

但是通过查看源码,发现都使用使用 ComponentFactoryResolver 来实现组件的新建,然后通过document.body.appendChild 添加到页面上的,

但是,angular 文档中提示,ComponentFactoryResolver 不推荐使用。

Deprecated: Angular no longer requires Component factories. Please use other APIs where Component class can be used directly.
Note: since v13, dynamic component creation via ViewContainerRef.createComponent does not require resolving component factory: component class can be used directly.

12

自己实现

没有了 ComponentFactoryResolver, 那么就使用 ViewContainerRef.createComponent

主要思路:

  1. 新建一个容器组件,获取到 ViewContainerRef

    @Component({
     selector: 'app-dialog-container',
     template: '',
     styles: [''],
    })
    export class DialogContainerComponent {
    
     constructor(
         private service: DialogService,
         private viewContainerRef: ViewContainerRef,
     ) {
         this.service.containerRef = this.viewContainerRef;
     }
    }

    1234567891011121314

  2. 新建一个全局的服务提供者,

interface IDialogRef {
    id: any;
    element: ComponentRef<any>;
}

@Injectable({
    providedIn: 'root'
})
export class DialogService {
    private dialogItems: IDialogRef[] = [];
    public containerRef: ViewContainerRef;

    constructor(
        private injector: Injector,
    ) {

    }

    /**
     * 加载loading
     * @param option 
     * @returns loading 的id, 使用 remove(id: any) 进行关闭
     */
    public loading(option?: DialogLoadingOption): any {
        option = Object.assign({}, option, {
            time: 2000,
            closeable: true,
        });
        return this.createDailog(DialogLoadingComponent, option);
    }

    /**
     * 创建组件
     * @param component 
     * @param option 
     * @returns 
     */
    private createDailog<T>(component: Type<T>, option: any): any {
        const dialogId = ++ DialogService.guid;
        if (!this.containerRef) {
            return;
        }
        const dialogInjector = new DialogInjector(new DialogPackage(option, dialogId), this.injector);
        const dialogRef = this.containerRef.createComponent(component, {
            injector: dialogInjector
        });
        this.dialogItems.push({
            id: dialogId,
            element: dialogRef
        });
        return dialogId;
    }

    /**
     * 删除组件
     * @param i 
     */
    private removeAt(i: number) {
        const item = this.dialogItems[i];
        this.dialogItems.splice(i, 1);
        const dialogRef = item.element;
        dialogRef.destroy();
    }
}

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364

作为对比,ViewContainerRef.createComponentComponentFactoryResolver 使用更简单,而且更符合 angular 特色

constructor(
        private resolver: ComponentFactoryResolver,
        private applicationRef: ApplicationRef,
        private injector: Injector, 
        @Inject(DOCUMENT) private document: Document,
    ) { }


// 添加组件
const dialogFactory = this.resolver.resolveComponentFactory(component);
const dialogRef = dialogFactory.create(dialogInjector);
this.applicationRef.attachView(dialogRef.hostView);
this.document.body.appendChild(dialogRef.location.nativeElement);


// 删除组件
this.applicationRef.detachView(dialogRef.hostView);
this.document.body.removeChild(dialogRef.location.nativeElement);
this.dialogItems.splice(i);

12345678910111213141516171819

  1. 在组件内部实现删除功能
export class DialogMessageComponent implements OnDestroy {
    constructor(
        private data: DialogPackage<DialogMessageOption>,
        private service: DialogService,
    ) {

    }

    @HostListener('click')
    public close() {
        this.service.remove(this.data.dialogId);
    }
}

12345678910111213

  1. 以模块的方式提供
@NgModule({
    imports: [
        CommonModule,
    ],
    declarations: [
        ...COMPONENTS
    ],
    exports: [
        ...COMPONENTS
    ],
})
export class DialogModule {
    static forRoot(): ModuleWithProviders<DialogModule> {
        return {
            ngModule: DialogModule,
            providers: [
                DialogService
            ]
        };
    }
}

123456789101112131415161718192021

  1. 使用

第一步,在 app.module.ts 中导入

DialogModule.forRoot(),

1

第二步,在 app.component.ts 页面中添加容器

<app-dialog-container></app-dialog-container>

1

第三步,使用

constructor(
    private toastrService: DialogService) {
    this.toastrService.loading();
}

1234

总结

ViewContainerRef.createComponentComponentFactoryResolver 使用更简单,而且更符合 angular 特色,始终保持所有创建的内容在框架内。

完整代码请查看 Angular-ZoDream

转载请保留原文链接: https://zodream.cn/blog/id/231.html