Angular 4.2
with Typescript 2.3
I am refactoring a service that is responsible for creating a new script tag and adding it to the document.
Here is the old code:
loadScript(src:string){
const script = document.createElement('script');
document.body.appendChild(script);
script.src = src;
}
Now, I'd like to use the Renderer2
to avoid doing direct DOM manipulation. So I've injected what I need in my service and updated the code:
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;
}
However, I run into this error:
Error: no provider for Renderer2!
The service belongs to a CoreModule
whose only import is CommonModule
from @angular/common
This plunkr demonstrates the problem
Possibly duplicating with Using Renderer in Angular 4
You cannot inject Renderer2
, but we can run RendererFactory2
to get Renderer2
instance inside @Injectable()
service.
There are two different ways of solving this issue.
There is the way which Angular using internally in webworkers, for example. I've solved the problem with the code below:
import { Injectable, Renderer2, RendererFactory2 } from '@angular/core';
@Injectable()
class Service {
private renderer: Renderer2;
constructor(rendererFactory: RendererFactory2) {
this.renderer = rendererFactory.createRenderer(null, null);
}
}
In your particular case, it will be
@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;
}
}
Parameters of RendererFactory2.createRenderer
method are:
hostElement
with type any
type
with type RendererType2|null
You can see that (null, null)
parameters are here:
https://github.com/angular/angular/blob/e3140ae888ac4037a5f119efaec7b1eaf8726286/packages/core/src/render/api.ts#L129
// 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;
}
}