Angular Universal:未定义导航器

Posted

技术标签:

【中文标题】Angular Universal:未定义导航器【英文标题】:Angular Universal: navigator is not defined 【发布时间】:2018-09-25 02:29:49 【问题描述】:

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

我可以为我的 angular-cli 应用程序执行 s-s-r。但是当我尝试集成ngx-leaflet 时,我收到以下错误:

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

现在,我了解到传单正在尝试访问节点上下文中不可用的导航器对象。所以我决定延迟传单渲染,直到页面加载到浏览器中,如SO thread 中给出的那样。 但我仍然遇到同样的错误。您可以查看带有传单问题here 的演示应用程序。

./src/app/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/app/leaflet/app/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/app.component.html:

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

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

编辑:

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

./src/app/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/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 模块导入?

【问题讨论】:

不确定是否可行,但请尝试&lt;div *ngIf="isBrowser" leaflet [leafletOptions]="options"&gt;&lt;/div&gt; 还是同样的问题。 这是您唯一使用它的地方吗?另一个解决方案是不在你的 module.server.ts 文件中导入 ngx-leaflet 模块 我没有在 app.server.module.ts 中导入 ngx-leaflet,只在 app.module.ts 中导入。 在你的 app.server.module 中你没有导入 'app.module' ?如果不是,那么这意味着 ngx-lealeft 可能不是罪魁祸首。您应该查看 server.js 以尝试查看它的确切来源 【参考方案1】:

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

server.ts:

const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();


global['navigator'] = mock.getNavigator();

【讨论】:

【参考方案2】:

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

【讨论】:

win.navigator 中的win 实际上是window?我现在在运行我的角度 s-s-r 时遇到了这个问题。 @BelleZaid 我不记得所有的细节,除了它解决了:) 更多:github.com/angular/universal/issues/830#issuecomment-770369651 感谢@Daniel。在此之前我确实尝试过该问题中的评论,但仍然有导航器未定义错误。我对角度通用的东西很陌生。所以我找不到太多关于为什么这种情况持续存在的原因。 @BelleZaid 很遗憾听到,但 Angular Universal 的开发人员体验真的很差... 这是如果你使用多米诺,这需要你导入一个巨大的包【参考方案3】:

我也收到了 Angular Universal 的这个错误。使用上述解决方案中的模拟浏览器确实为我解决了这个错误,但它也启动了一个与 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;

【讨论】:

以上是关于Angular Universal:未定义导航器的主要内容,如果未能解决你的问题,请参考以下文章

Angular Universal 错误 ReferenceError:未定义 MouseEvent

Angular5 webpack ReferenceError:未定义窗口

在 Angular 中需要 BrowserAnimationsModule 但在 Universal 中出现错误

Angular 2 Universal 404 Not Found 重定向

在 ag-grid 中使用自定义单元格渲染器时无法读取未定义的属性“导航”

Angular 9 通用错误:组件“HeaderComponent”未解决: