Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service: No provider for Renderer2

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

like image 700
BeetleJuice Avatar asked Jul 08 '17 18:07

BeetleJuice


People also ask

When should I use Renderer2?

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.

What is rendered in Angular?

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.

Why should we use Renderer in Angular?

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.


2 Answers

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

  • Using Renderer in Angular 4
like image 23
yurzui Avatar answered Sep 23 '22 05:09

yurzui


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.

Get an instance of Renderer2 inside Service

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

Pass Renderer2 from component

// 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;   } } 
like image 134
Eugene Gavrilov Avatar answered Sep 21 '22 05:09

Eugene Gavrilov