Сервис: нет провайдера для Renderer2

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!

Служба принадлежит CommonModule CoreModule, единственным импортом которого является CommonModule из @angular/common

Этот планкр демонстрирует проблему

Ответ 1

Вы можете инициализировать службу с помощью экземпляра Renderer2 в корневом компоненте

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

См. также

Ответ 2

Возможно дублирование с использованием Renderer в Angular 4

Вы не можете внедрить Renderer2, но мы можем запустить RendererFactory2 чтобы получить экземпляр Renderer2 внутри @Injectable(). Есть два разных способа решения этой проблемы.

Получить экземпляр 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;
  }
}

Ответ 3

Я попытался реализовать это с помощью render2, но в сервисе - что привело к "StaticInjectorError (AppModule) [Renderer2]" -Error, поскольку внедрение Renderer2-Instance, по-видимому, невозможно. Решением было внедрить RendererFactory2 и manullay создать ссылку в сервисе, например:

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