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
The Renderer2 allows us to manipulate the DOM elements, without accessing the DOM directly. It provides a layer of abstraction between the DOM element and the component code. Using Renderer2 we can create an element, add a text node to it, append child element using the appendchild method., etc.
By default, Angular renders a template into DOM. You can use custom rendering to intercept rendering calls, or to render to something other than DOM.
Using the Renderer for manipulating the DOM doesn't break server-side rendering or Web Workers (where direct access to the DOM would break). The Renderer2 class is an abstraction provided by Angular in the form of a service that allows to manipulate elements of your app without having to touch the DOM directly.
You can initialize service with an instance of Renderer2
in root component
@Injectable() class MyService { renderer : Renderer2; } ... class App { name:string; constructor(service: MyService, renderer: Renderer2) { service.renderer = renderer; } }
See also
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; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With