【问题标题】:Angular5: Dynamic component loading - StaticInjector error with @angular/core dependenciesAngular5:动态组件加载 - 带有 @angular/core 依赖项的 StaticInjector 错误
【发布时间】:2018-04-30 18:06:52
【问题描述】:

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

我像这样动态加载组件:

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

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

那么,

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

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

模板:

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

错误:

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

模板:

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

错误:

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

Material 模块似乎很好地导入到我的插件模块中,否则如果它没有加载我有不同的行为:它忽略指令(例如 &lt;button mat-button&gt;&lt;/button&gt; 只是一个 &lt;button&gt;&lt;/button&gt; 没有材质效果且没有错误)和材质组件抛出错误(例如&lt;mat-card&gt;test&lt;mat-card&gt; 抛出Error: Template parse errors: 'mat-card' is not a known element...)

我尝试过以多种不同方式捆绑动态模块: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

【问题讨论】:

  • 你能设置复制吗?
  • 感谢转载。您遇到的问题与您的应用程序加载两个 core.umd.js 文件的事实有关。当 Angular 解决依赖关系时,它将 ElementRef 类存储为键。然后使用这个密钥。由于您有 ElementRef 的两个定义,因此角度无法解决提供者。 take.ms/Vq5F0
  • Systemjs 将core.umd.js 加载为file:///...
  • 我已成功使用window 共享@angular/* 模块以在全球范围内存储模块。它并不优雅,但它有工作的优点......在我的插件中导入角度核心我使用const NgModule = window.angular.core.NgModule而不是传统的import {NgModule} from "@angular/core",在我的index.html中,我在窗口中导入所有需要共享的模块(@angular/core@angular/common 等..)。我还阅读/观看了 Angular Elements,这似乎非常适合这个用例:youtu.be/ljsOPm4MMEo?t=13m20s.

标签: angular angular-material2


【解决方案1】:

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

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

window.angular = {
    core: require("@angular/core"),
    common: require("@angular/common"),
    material: require("@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

【讨论】:

    猜你喜欢
    • 2018-07-21
    • 1970-01-01
    • 2017-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多