Сервис: нет провайдера для 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 );
}
}