Angular 通用:未定义导航器
Angular Universal: navigator is not defined
我按照官方 angular-cli tutorial 将 angular-universal 集成到我现有的 angular-cli 应用程序中。
我可以为我的 angular-cli 应用程序执行 SSR。但是当我尝试集成 ngx-leaflet 时,出现以下错误:
ReferenceError: navigator is not defined
at D:\ng2-ssr-pwa\dist\server.js:40251:29
现在,我了解到 leaflet 正在尝试访问节点上下文中不可用的导航器对象。所以我决定延迟传单呈现,直到页面加载到浏览器中,如 中所示。
但我仍然遇到同样的错误。您可以查看带有传单问题的演示应用程序 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,传单。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 模块导入?
使用 Mock Browser 解决了这个问题。
server.ts:
const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();
global['navigator'] = mock.getNavigator();
由 (global as any).navigator = win.navigator;
修复,更加优雅,绝对原生,并且不依赖过时的 Mock Browser。
我也收到了 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-cli tutorial 将 angular-universal 集成到我现有的 angular-cli 应用程序中。
我可以为我的 angular-cli 应用程序执行 SSR。但是当我尝试集成 ngx-leaflet 时,出现以下错误:
ReferenceError: navigator is not defined at D:\ng2-ssr-pwa\dist\server.js:40251:29
现在,我了解到 leaflet 正在尝试访问节点上下文中不可用的导航器对象。所以我决定延迟传单呈现,直到页面加载到浏览器中,如
./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,传单。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 模块导入?
使用 Mock Browser 解决了这个问题。
server.ts:
const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();
global['navigator'] = mock.getNavigator();
由 (global as any).navigator = win.navigator;
修复,更加优雅,绝对原生,并且不依赖过时的 Mock Browser。
我也收到了 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;