簡體   English   中英

服務:沒有 Renderer2 的提供者

[英]Service: No provider for Renderer2

Angular 4.2Typescript 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 ,其唯一導入是來自@angular/commonCommonModule

這個 plunkr演示了這個問題

可能與Using Renderer in Angular 4重復

您不能注入Renderer2 ,但我們可以運行RendererFactory2以在@Injectable()服務中獲取Renderer2實例。 有兩種不同的方法可以解決這個問題

在 Service 中獲取 Renderer2 的實例

例如,Angular 在 webworkers 內部使用的方式。 我已經用下面的代碼解決了這個問題:

import { Injectable, 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主機元素
  • typeRendererType2|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;
  }
}

您可以使用根組件中的Renderer2實例初始化服務

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

也可以看看

我嘗試使用 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 );
  }
}

重要的提示:

無論哪種方法適合您的需求,重要的是要認識到Renderer2可以有許多不同的實現DefaultDomRenderer2BaseAnimationRendererDebugRenderer2EmulatedEncapsulationDomRenderer2等。例如,每個使用 ViewEncapsulation 的組件都有不同的渲染器。

如果您在兄弟組件提供的服務中注入 Renderer2,那么您將自動獲得該組件的渲染器。 這對於 ViewEncapsulation 很重要,因為它是真正知道如何生成那些_ngcontent-appName-c339屬性的渲染器。

如果您在根級別、祖先組件或您的AppComponent注入Renderer2 ,然后嘗試使用它來生成 HTML,您要么沒有主機屬性,要么更糟糕的是得到錯誤的主機屬性。

如果也使用 SSR 或網絡工作者,請務必仔細測試您的期望。

這些對於您正在做的事情可能都不重要,但重要的是要了解存在不同Renderer2實例的原因。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM