Renderer2 does not get resolved to the custom renderer provided via DI
See original GitHub issueI’m submitting a Bug report
[x] Bug report
Current behavior
Standard NgModule’s DomRendererFactory2
and DefaultDomRenderer2
are being used instead of the ones provided via dependency injection.
Expected behavior
RendererFactory2
to be resolved to custom MyDomRendererFactory2
.
Renderer2
to be resolved to MyRenderer
.
What is the motivation / use case for changing the behavior?
I am trying to render Angular module/root component into a non-html structure.
Environment
- Angular version: 6.1.4
- Browser: NOT Applicable -- running in Node.js environment
- Node version: 8.9.3
- Platform: Windows
Minimal reproduction of the problem with instructions
Run the CustomModuleRenderer.ts
code under debugger (I use VS Code).
The source code is available here: https://github.com/another-guy/protractor-angular-seattle/commit/a6de073fbc8303bea27621ca059ebff2399b79b8 (a6de073fb
is the target commit)
CustomModuleRenderer.ts
import { DOCUMENT } from '@angular/common';
import { ResourceLoader } from '@angular/compiler';
import { CompilerFactory, COMPILER_OPTIONS, Injectable, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2, StaticProvider, Type, NgZone } from '@angular/core';
import { EventManager } from '@angular/platform-browser';
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
import { platformServer } from '@angular/platform-server';
import { readFile } from 'fs';
import * as glob from 'glob';
import { JSDOM } from 'jsdom';
import * as path from 'path';
import 'zone.js';
import 'zone.js/dist/long-stack-trace-zone.js';
import { AppComponent } from './app/app.component';
import { AppModule } from './app/app.module';
const jsdom = new JSDOM(`<html></html>`);
export function _document() {
return jsdom.window.document;
}
@Injectable()
export class ResourceLoaderImpl extends ResourceLoader {
constructor(private _cwd: string) {
super();
}
get(resourceFileName: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
glob(`**/${resourceFileName}`, { cwd: this._cwd, }, (globError, matches) => {
if (globError) reject(globError.toString());
else
readFile(path.join(this._cwd, matches[0]), (readFileError, data) => {
if (readFileError) reject(readFileError);
else resolve(data.toString());
});
});
});
}
}
@Injectable()
export class MyRenderer implements Renderer2 {
data: { [key: string]: any; };
destroy(): void { }
createElement(name: string, namespace?: string) { }
createComment(value: string) { }
createText(value: string) { }
destroyNode: (node: any) => void;
appendChild(parent: any, newChild: any): void { }
insertBefore(parent: any, newChild: any, refChild: any): void { }
removeChild(parent: any, oldChild: any): void { }
selectRootElement(selectorOrNode: any) { }
parentNode(node: any) { }
nextSibling(node: any) { }
setAttribute(el: any, name: string, value: string, namespace?: string): void { }
removeAttribute(el: any, name: string, namespace?: string): void { }
addClass(el: any, name: string): void { }
removeClass(el: any, name: string): void { }
setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void { }
removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void { }
setProperty(el: any, name: string, value: any): void { }
setValue(node: any, value: string): void { }
listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void { return null; }
constructor(private eventManager: EventManager) {}
}
@Injectable()
export class MyDomRendererFactory2 implements RendererFactory2 {
private renderer: MyRenderer;
constructor(eventManager: EventManager) {
this.renderer = new MyRenderer(eventManager);
}
createRenderer(hostElement: any, type: RendererType2): Renderer2 {
return this.renderer;
}
begin?(): void { throw new Error("Method not implemented."); }
end?(): void { throw new Error("Method not implemented."); }
whenRenderingDone?(): Promise<any> { throw new Error("Method not implemented."); }
}
async function generate<M>(moduleType: Type<M>) {
try {
const documentProvider = { provide: DOCUMENT, useFactory: _document, deps: [] };
const commonPlatformServerProviders: StaticProvider[] = [
documentProvider,
];
const rendererFactoryProvider = {
provide: RendererFactory2, useFactory: MyDomRendererFactory2,
deps: [DOCUMENT, EventManager, NgZone], // DomRendererFactory2, AnimationEngine, NgZone
};
const rendererProvider = { provide: Renderer2, useClass: MyRenderer, deps: [EventManager, DOCUMENT] };
const platformRef = platformServer([
...commonPlatformServerProviders,
{
provide: COMPILER_OPTIONS,
useValue: {providers: [{provide: ResourceLoader, useClass: ResourceLoaderImpl, deps: []}]},
multi: true
},
{ provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] },
rendererFactoryProvider,
rendererProvider,
]);
const resourceLoaderProvider = { provide: ResourceLoader, useValue: new ResourceLoaderImpl(`src\\app`), deps: [] };
const moduleRef = await platformRef
.bootstrapModule(
moduleType,
{
providers: [
documentProvider,
rendererFactoryProvider,
rendererProvider,
resourceLoaderProvider,
],
});
const appComponent = moduleRef.injector.get(AppComponent);
console.info(appComponent.title.toString());
} catch (error) {
const errorText = error.toString();
throw new Error(errorText);
}
}
generate(AppModule)
.then(message => console.info({ message }))
.catch(error => console.error({ error }));
AppModule.ts
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { TimesheetFooterLineComponent } from './timesheet-footer-line/timesheet-footer-line.component';
import { TimesheetHeaderLineComponent } from './timesheet-header-line/timesheet-header-line.component';
import { TimesheetWeekDayComponent } from './timesheet-week-day/timesheet-week-day.component';
import { TimesheetWeekLineComponent } from './timesheet-week-line/timesheet-week-line.component';
import { ResourceLoader } from '@angular/compiler';
@NgModule({
declarations: [
AppComponent,
TimesheetWeekLineComponent,
TimesheetHeaderLineComponent,
TimesheetFooterLineComponent,
TimesheetWeekDayComponent
],
imports: [
BrowserModule,
FormsModule,
],
providers: [
{ provide: ResourceLoader, useValue: null },
],
bootstrap: [AppComponent]
})
export class AppModule { }
Issue Analytics
- State:
- Created 5 years ago
- Reactions:3
- Comments:7 (3 by maintainers)
Top Results From Across the Web
angular - Renderer cannot be used in Service? - Stack Overflow
You cannot inject Renderer2 , but we can run RendererFactory2 to get Renderer2 instance inside @Injectable() service. There are two different ways of ......
Read more >Renderer to Renderer2 migration - Angular
This section provides guidance on migrating from this deprecated API to the newer Renderer2 API and what it means for your app. Why...
Read more >Using Renderer2 in Angular - DigitalOcean
The Renderer2 class is an abstraction provided by Angular in the form of a service that allows to manipulate elements of your app...
Read more >Angular - @angular/core
Creates and initializes a custom renderer that implements the Renderer2 base class. ... The DI framework provides null if the dependency is not...
Read more >@angular/core - Angular 12 - W3cubDocs
These functions are not exposed when the application runs in a production mode. ... Creates and initializes a custom renderer that implements the...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@another-guy You can just provide a minimal reproduction in https://stackblitz.com/ so others can have a look.
Works as intended.
Renderer2
are not injectable and they get created throughRendererFactory2
it is that which you need to override.