服务:没有Renderer2的提供者

时间:2017-07-08 18:49:00

标签: angular typescript

带有Angular 4.2

Typescript 2.3

我正在重构一项服务,该服务负责创建新的脚本标记并将其添加到文档中。

这是旧代码:

loadScript(src:string){
    const script = document.createElement('script');
    document.body.appendChild(script);
    script.src = src;
}

现在,我想使用Renderer2来避免直接进行DOM操作。所以我在服务中注入了我需要的内容并更新了代码:

constructor(private renderer:Renderer2, @Inject(DOCUMENT) private document){}

loadScript(src:string){
    const script = this.renderer.createElement('script');
    this.renderer.appendChild(this.document.body,script);
    script.src = src;
}

但是,我遇到了这个错误:

  

错误:没有Renderer2的提供者!

该服务属于CoreModule,其唯一导入为CommonModule来自@angular/common

This plunkr演示了问题

4 个答案:

答案 0 :(得分:27)

可能与Using Renderer in Angular 4

重复

您无法注入Renderer2,但我们可以运行RendererFactory2来获取Renderer2服务中的@Injectable()实例。 有两种不同的解决方法issue

在服务

中获取Renderer2的实例

例如,Angular在网络工作者内部使用的方式。 我已用以下代码解决了问题:

import { Renderer2, RendererFactory2 } from '@angular/core';

@Injectable()
class Service {
    private renderer: Renderer2;

    constructor(rendererFactory: RendererFactory2) {
        this.renderer = rendererFactory.createRenderer(null, null);
    }
}

在您的特定情况下,它将是

@Injectable()
class Service {
  private renderer: Renderer2;

  constructor(rendererFactory: RendererFactory2, @Inject(DOCUMENT) private document){
    this.renderer = rendererFactory.createRenderer(null, null);
  }

  loadScript(src:string){
    const script = this.renderer.createElement('script');
    this.renderer.appendChild(this.document.body,script);
    script.src = src;
  }
}

RendererFactory2.createRenderer方法的参数是:

    类型为hostElement
  • any 类型为type
  • RendererType2|null

您可以在此处看到(null, null)个参数: https://github.com/angular/angular/blob/e3140ae888ac4037a5f119efaec7b1eaf8726286/packages/core/src/render/api.ts#L129

从组件

传递Renderer2
// declare public property in your service
@Injectable()
class Service {
  renderer: Renderer;
}

// pass renderer to service in your component file
class App {
  name:string;
  constructor(service: Service, renderer: Renderer2) {
      service.renderer = renderer;
  }
}

答案 1 :(得分:13)

您可以使用根组件中的Renderer2实例初始化服务

@Injectable()
class MyService {
  renderer : Renderer2;
}
...
class App {
  name:string;
  constructor(service: MyService, renderer: Renderer2) {
      service.renderer = renderer;
  }
}

另见

答案 2 :(得分:0)

我尝试使用render2实现此功能,但是在服务中-导致'StaticInjectorError(AppModule)[Renderer2]'-错误,因为似乎无法注入Renderer2-Instance。解决方案是注入RendererFactory2并手动在服务中创建引用,例如:

@Injectable({
  providedIn: 'root'
})
export class FgRendererService {
  /** Reference to render-instance */
  public renderer: Renderer2;
  /** CONSTRUCTOR */
  constructor(
    private _renderer: RendererFactory2
  ) {
    this.renderer = _renderer.createRenderer(null, null);
  }
  /** Add class to body-tag */
  addBodyClass( classToAdd: string ): void {
    this.renderer.addClass( document.body, classToAdd );
  }
  /** Remove class from body-tag */
  removeBodyClass( classToRemove: string ): void {
    this.renderer.removeClass( document.body, classToRemove );
  }
}

答案 3 :(得分:0)

重要说明:

无论哪种方法适合您的需求,重要的是要认识到 Renderer2 可以有许多不同的实现 DefaultDomRenderer2BaseAnimationRendererDebugRenderer2EmulatedEncapsulationDomRenderer2 等。例如,每个使用 ViewEncapsulation 的组件都会获得不同的渲染器。

如果您将 Renderer2 注入到由同级组件提供的服务中,那么您将自动获得该组件的渲染器。这对于 ViewEncapsulation 很重要,因为渲染器实际上知道如何生成定义样式范围的 _ngcontent-appName-c339 属性。

如果您在根级别、祖先组件或从您的 Renderer2 注入 AppComponent,然后尝试使用它来生成 HTML,您要么没有主机属性,要么得到错误的属性。

如果也使用 SSR 或网络工作者,请务必仔细测试您的期望。

这些对于您正在执行的操作可能无关紧要,但重要的是要了解存在 Renderer2 不同实例的原因。