Issue
I need to implement removeAllClasses()
and addClasses(string[])
methods by extending Renderer2.
import { Injectable, Renderer2 } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export abstract class RendererExtended extends Renderer2 (
protected constructor() {
super();
}
public removeAllClasses(el: any) {
this.removeAttribute(el, 'class');
}
}
And when I try to use it in component:
constructor(
private renderer: RendererExtended) {
}
private showOrHideArrow() {
this.renderer.removeAllClasses(this.iconComponentRef.location.nativeElement);
this.renderer.addClass(this.iconComponentRef.location.nativeElement, 'hovered');
}
I see an error:
ERROR TypeError: _this.renderer.addClass is not a function
Solution
It’s a little bit more complicated than that. Renderer2 is abstract since Angular doesn’t have to use DOM renderer. It could use some other custom renderer and render templates somewhere else.
When you inject Renderer2 into your component, Angular uses the injection token Renderer2Interceptor
and looks for a proper provider. By default it would be DefaultDomRenderer2
(look here) that is provided via the factory, but it could be something else.
If you want to add your custom renderer, you can read about it in official docs. You could create one that extends the DefaultDomRenderer2
and adds your custom logic on top of that, but that’s a bit of a hassle, especially if you’re not sure what you’re doing.
Instead, the easy way around could be creating a service that injects the RendererFactory2
to create the renderer, i.e.
import { Injectable, Renderer2, RendererFactory2} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class RendererWrapperService (
renderer2: Renderer2;
constructor(readonly rendererFactory: RendererFactory2) {
this.rendererFactory.createRenderer(null, null);
}
public removeAllClasses(el: any) {
this.renderer2.removeAttribute(el, 'class');
}
}
The renderer2 it injected as public property, so if you inject your service into your component you should still be able to use it, e.g.
constructor(private readonly rendererEx: RendererWrapperService ) {
}
private showOrHideArrow() {
this.rendererEx.removeAllClasses(this.iconComponentRef.location.nativeElement);
this.rendererEx.renderer2.addClass(this.iconComponentRef.location.nativeElement, 'hovered');
}
That being said, you’re swimming against the current trying to manipulate DOM manually. You’re better off using stuff like ngClass or similar. I assume you’re hacking around it since you’re using some external library / package that doesn’t expose way to manipulate it’s styling. But if you’re experiencing such issues early on in development, you’re probably better off ditching that library and writing the feature yourself instead. But that’s just my two cents.
EDIT: Updated the answer as per comment – Renderer2
gets injected per component and will not be provided at the root level so it wouldn’t be available at the service level. RendererFactory2
can be injected instead. Updated the answer