Angular Universal:未定义导航器

时间:2018-04-15 05:43:42

标签: javascript angular angular-cli angular-universal ngx-leaflet

我按照官方的角度cli tutorial将角度通用集成到我现有的angular-cli应用程序中。

我可以为我的angular-cli app做SSR。但是当我尝试整合ngx-leaflet时,我收到了以下错误:

  

ReferenceError:未定义导航器       在D:\ ng2-ssr-pwa \ dist \ server.js:40251:29

现在,我知道传单正在尝试访问Node上下文中不可用的导航器对象。所以我决定推迟传单呈现,直到在浏览器中加载页面为SO thread中给出的内容。 但我仍然得到同样的错误。您可以查看带有小册子问题here的演示应用程序。

./ SRC /应用/ browserModuleLoader.service.ts:

import { Component, Inject, Injectable, OnInit, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

@Injectable()
export class BrowserModuleLoaderService {
    private _L: any;

    public constructor(@Inject(PLATFORM_ID) private _platformId: Object) {
        this._init();
    }

    public getL() {
        return this._safeGet(() => this._L);
    }

    private _init() {
        if (isPlatformBrowser(this._platformId)) {
            this._requireLegacyResources();
        }
    }

    private _requireLegacyResources() {
        this._L = require('leaflet');
    }

    private _safeGet(getCallcack: () => any) {
        if (isPlatformServer(this._platformId)) {
            throw new Error('invalid access to legacy component on server');
        }

        return getCallcack();
    }
}

./ SRC /应用/小叶/应用/ leaflet.component.ts:

// import * as L from 'leaflet';

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, PLATFORM_ID } from '@angular/core';

import { BrowserModuleLoaderService } from '../browserModuleLoader.service';
import { isPlatformBrowser } from '@angular/common';

@Component({
    selector: 'app-leaflet',
    styleUrls: ['./leaflet.component.scss'],
    template: `
      <div  *ngIf="isBrowser">
        <div leaflet [leafletOptions]="options"></div>
        </div>
  `,
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LeafletComponent {
    isBrowser: boolean;
    options = {};

    constructor(private cdr: ChangeDetectorRef,
        @Inject(PLATFORM_ID) platformId: Object,
        private browserModuleLoaderService: BrowserModuleLoaderService
    ) {
        this.isBrowser = isPlatformBrowser(platformId);
    }

    ngAfterViewInit() {
        console.log('this.isBrowser ', this.isBrowser);
        if (this.isBrowser) {
            const L = this.browserModuleLoaderService.getL();
            this.options = {
                layers: [
                    L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' }),
                ],
                zoom: 5,
                center: L.latLng({ lat: 38.991709, lng: -76.886109 }),
            };
        }
        this.cdr.detach();
    }

}

./ SRC /应用/ app.component.html:

<div>
  <app-leaflet></app-leaflet>
</div>

如何安全地延迟传单呈现,直到平台不是浏览器?

修改

我删除了与leaflet相关的所有代码(browserModuleLoader.service.ts,leaflet.component.ts等),并在app.module.ts中仅保留了传单模块导入,实际上这导入导致了问题。

./ SRC /应用/ app.module.ts:

import { AppComponent } from './app.component';
import { BrowserModule } from '@angular/platform-browser';
// import { BrowserModuleLoaderService } from './browserModuleLoader.service';
// import { LeafletComponent } from './leaflet/leaflet.component';
import { LeafletModule } from '@asymmetrik/ngx-leaflet';
import { NgModule } from '@angular/core';

@NgModule({
  declarations: [
    AppComponent,
    // LeafletComponent
  ],
  imports: [
    BrowserModule.withServerTransition({appId: 'my-app'}),
    LeafletModule.forRoot()
  ],
  providers: [
    // BrowserModuleLoaderService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

./ SRC /应用/ app.server.module.ts:

import {AppComponent} from './app.component';
import {AppModule} from './app.module';
import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader';
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
    ModuleMapLoaderModule
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

如何处理此nxg-leaflet模块导入?

3 个答案:

答案 0 :(得分:4)

使用Mock Browser解决了这个问题。

<强> server.ts:

'"folder"'

答案 1 :(得分:0)

(global as any).navigator = win.navigator; 修复,更加优雅,绝对是原生的,并且不依赖过时的 Mock Browser

答案 2 :(得分:0)

我也收到了 Angular Universal 的这个错误。使用上面解决方案中的 Mock Browser 确实为我修复了这个错误,但它也启动了一个与 CommonJS 相关的新警告。我没有深入研究这个问题,而是意识到我已经在我的 server.ts 文件中使用了 Domino,因此我可以轻松地使用 Domino 设置导航器。这是最适合我的方法:

npm install domino

server.ts:

const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs
  .readFileSync(path.join('dist/<your-app-name>/browser', 'index.html')) //<--- REPLACE WITH YOUR APP NAME
  .toString();
const window = domino.createWindow(template);
global['window'] = window;
global['document'] = window.document;
global['navigator'] = window.navigator;