question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Renderer2 does not get resolved to the custom renderer provided via DI

See original GitHub issue

I’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:closed
  • Created 5 years ago
  • Reactions:3
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
trotylcommented, Sep 19, 2018

@another-guy You can just provide a minimal reproduction in https://stackblitz.com/ so others can have a look.

1reaction
mheverycommented, Sep 11, 2018

Works as intended.

Renderer2 are not injectable and they get created through RendererFactory2 it is that which you need to override.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found