Angular5:动态组件加载 - 带有 @angular/core 依赖项的 StaticInjector 错误 [英] Angular5: Dynamic component loading - StaticInjector error with @angular/core dependencies

查看:26
本文介绍了Angular5:动态组件加载 - 带有 @angular/core 依赖项的 StaticInjector 错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将一个电子应用程序从 AngularJS 迁移到 Angular5,并且我在需要加载任意模块及其组件并将主要组件动态呈现到视图的部分中遇到问题(它是一个插件系统).在我的应用程序中,插件是一个 angular5 模块,其中包含在我的 angular5 应用程序中呈现的组件.

我像这样动态加载组件:

@ViewChild("view", { read: ViewContainerRef })视图:ViewContainerRef;构造函数(//...私有编译器:编译器,私人注射器:注射器,private moduleRef: NgModuleRef) {}

那么,

const addonModule = addonObject.getModule();this.compiler.compileModuleAndAllComponentsAsync(addonModule).then(工厂 => {const factory = factory.componentFactories.find(组件工厂 =>addonObject.getViewComponent().name ==componentFactory.componentType.name);如果(工厂){const cmpRef = factory.create(this.injector, [], null, this.moduleRef);cmpRef.instance.config = {来自:afd@test.com",apikey:dsfkjd"};this.view.insert(cmpRef.hostView);}});

效果很好,但是当我在模板中添加 Material Component 时,它在 factory.create() 处失败并出现以下错误:

模板:

<button mat-button>test</button>

错误:

ERROR Error: Uncaught (in promise): Error: StaticInjectorError[Renderer2]:静态注入器错误[Renderer2]:NullInjectorError: Renderer2 没有提供程序!

模板:

测试

错误:

ERROR Error: Uncaught (in promise): Error: StaticInjectorError[MediaMonitor]:静态注入器错误[MediaMonitor]:NullInjectorError: MediaMonitor 没有提供程序!

材料模块似乎很好地导入到我的插件模块中,否则如果它没有加载我有不同的行为:它忽略指令(例如 <button mat-button></button> 只是一个<button></button> 没有材质效果和错误)和材质组件抛出错误(例如 test throws错误:模板解析错误:mat-card"不是已知元素...)

我尝试以多种不同的方式捆绑动态模块:tsc/ngc/ng-packagr/webpack.

当我在另一个使用 ng-cli 构建的应用程序的 NgModule 中正常(静态)导入它时,该包正在工作.

有人知道如何动态渲染模板中使用了材料组件/指令的组件,例如 fxFlex/mat-button/mat-card 吗?

我在这里用 SystemJS 复制了这个错误:https://github.com/thyb/repro-dynamic-loading

角度:5.0.2

角材质:5.0.0-rc1

解决方案

正如@yurzui 在评论中解释的那样,@angular/* 依赖项被加载了两次.作为一种解决方法,我发现使其工作的唯一方法是使用 window 在应用程序和插件之间共享 @angular 对象(@angular/core,<代码>@angular/common, @angular/material).

在我的应用程序 index.html 中:

window.angular = {核心:需要(@angular/core"),常见:需要(@angular/common"),材料:需要(@angular/material"),...}

在我的插件中:

const NgModule = (window as any).angular.core.NgModule;const CommonModule = (window as any).angular.common.CommonModule;const MatButtonModule = (window as any).angular.material.MatButtonModule;...

这不是很优雅(再见摇树)但至少它有效......如果有人有更简单的解决方案,我会接受:)

我在 Gitter 上看到了一个关于 @angular/element 的讨论,其中看起来很有前途,因为它创建了独立/自包含的 Web 组件,可以动态注入而不会遇到我遇到的问题 - https://www.youtube.com/watch?v=ljsOPm4MMEo&feature=youtu.be&t=13m20s.这是一个实验室项目,因此在当前版本中不可用,但已经有一些工作:https://github.com/angular/angular/pull/19469

I am migrating an electron application from AngularJS to Angular5 and I am facing issues in a part where I need to load an arbitrary module with its components and render the main component to the view dynamically (it's an addon system). In my application, an addon is an angular5 module containing a component which is rendered in my angular5 application.

I load dynamically the component like this:

@ViewChild("view", { read: ViewContainerRef })
view: ViewContainerRef;

constructor(
  // ...
  private compiler: Compiler,
  private injector: Injector,
  private moduleRef: NgModuleRef<any>
) {}

then,

const addonModule = addonObject.getModule();
this.compiler
  .compileModuleAndAllComponentsAsync(addonModule)
  .then(factories => {
    const factory = factories.componentFactories.find(
      componentFactory =>
        addonObject.getViewComponent().name ==
        componentFactory.componentType.name
    );
    if (factory) {
      const cmpRef = factory.create(this.injector, [], null, this.moduleRef);
      cmpRef.instance.config = {
        from: "afd@test.com",
        apikey: "dsfkjd"
      };
      this.view.insert(cmpRef.hostView);
    }
  });

it works great but when I add Material Component in the template, it fails at factory.create() with this error:

template:

<div>
    <button mat-button>test</button>
</div>

error:

ERROR Error: Uncaught (in promise): Error: StaticInjectorError[Renderer2]: 
  StaticInjectorError[Renderer2]: 
    NullInjectorError: No provider for Renderer2!

or

template:

<div fxLayout="row" fxFlex>
    test
</div>

error:

ERROR Error: Uncaught (in promise): Error: StaticInjectorError[MediaMonitor]: 
  StaticInjectorError[MediaMonitor]: 
    NullInjectorError: No provider for MediaMonitor!

The Material modules seems well imported in my addon module else if it's not loaded I have different behaviors: it ignores directives (e.g. <button mat-button></button> is just a <button></button> without material effect and without errors) and material component throw errors (e.g. <mat-card>test<mat-card> throws Error: Template parse errors: 'mat-card' is not a known element...)

I have tried to bundle the dynamic module in many different ways: tsc / ngc / ng-packagr / webpack.

The bundle is working when I import it normally (static) in the NgModule of another app built with ng-cli.

Does someone know how to render dynamically a component which has material component/directive used in the template like fxFlex / mat-button / mat-card ?

EDIT: I have made a reproduction of the bug with SystemJS here: https://github.com/thyb/repro-dynamic-loading

Angular: 5.0.2

Angular Material: 5.0.0-rc1

解决方案

As @yurzui explained in comments, the @angular/* dependencies were loaded twice. As a workaround, the only way I found to make it work is by using window to share the @angular objects between the application and the addons (@angular/core, @angular/common, @angular/material).

in my application index.html:

window.angular = {
    core: require("@angular/core"),
    common: require("@angular/common"),
    material: require("@angular/material"),
    ...
}

and in my addons:

const NgModule = (window as any).angular.core.NgModule;
const CommonModule = (window as any).angular.common.CommonModule;
const MatButtonModule = (window as any).angular.material.MatButtonModule;
...

It's not really elegant (bye bye tree shaking) but at least it works... If someone has a simpler solution, I take it :)

I came across a discussion on Gitter about @angular/element which seems promising as it creates standalone/self contained web component which could be injected dynamically without the issues I encountered - https://www.youtube.com/watch?v=ljsOPm4MMEo&feature=youtu.be&t=13m20s. This is a lab project so not available in the current releases but there is already some work on it: https://github.com/angular/angular/pull/19469

这篇关于Angular5:动态组件加载 - 带有 @angular/core 依赖项的 StaticInjector 错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆