使用自定义渲染器渲染Angular模块:`ReferenceError:未定义文档`

时间:2018-08-30 19:32:27

标签: javascript angular typescript

问题

当我尝试使用PlatformRef::bootstrapModule(...)渲染Angular模块时,出现错误:

  

ReferenceError:未定义文档

很显然,Angular尝试使用document in Node.js environment where it's not present(因为它不是浏览器环境)。

问题

如何解决此问题?

代码

核心代码

@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 extraProviders: StaticProvider[] = [
      { provide: Compiler, useFactory: compilerFactory => compilerFactory.createCompiler(), deps: [CompilerFactory] },
    ];
    const platformRef = platformDynamicServer(extraProviders);
    const moduleRef = await platformRef
      .bootstrapModule(
        moduleType,
        {
          providers: [
            { provide: ResourceLoader, useValue: new ResourceLoaderImpl(`src\\app`), deps: [ ] },

            {
              provide: RendererFactory2,
              useClass: MyDomRendererFactory2,
              deps: [EventManager],
            },
          ],
        });

    const appComponent = moduleRef.injector.get(AppComponent);

    console.info(appComponent.title.toString());
  } catch (error) {
    throw new Error(error.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) {}
}

自定义资源加载器

通过“ url”(实际上不是URL)猜测源文件,并读取文件的所有文本。

@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());
          });
      });
    });
  }
}

进口

import { ResourceLoader } from '@angular/compiler';
import { Compiler, CompilerFactory, Injectable, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2, StaticProvider, Type } from '@angular/core';
import { EventManager } from '@angular/platform-browser';
import { platformDynamicServer } from '@angular/platform-server';
import { readFile } from 'fs';
import * as glob from 'glob';
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';

更新1

我还尝试提供像这样的custom document object

import { JSDOM } from 'jsdom';

const jsdom = new JSDOM(`<html></html>`);
export function document() { return jsdom.window.document; }

通过在DI中注册它:

{ provide: DOCUMENT, useFactory: document, deps: [] }

仍然出现相同的ReferenceError: document is not defined错误。

更新2

我的问题的根源是RendererFactory2无法解析为MyDomRendererFactory2。相反,使用了标准的NgModule的DomRendererFactory2 ...这是我在调试过程中在providers中观察到的:

enter image description here

1 个答案:

答案 0 :(得分:0)

如果您使用的是服务器端,则必须添加该平台是浏览器还是服务器,因为服务器端支持中不提供文档,窗口之类的关键字

您可以从角度使用isPlatformBrowser。 https://angular.io/api/common/isPlatformBrowser