Angular 通用客户端似乎不是 运行
Angular Universal client side doesn't seem to be running
当运行我转换为服务器端呈现应用程序时,我的应用程序运行并呈现服务器端,但似乎没有客户端应用程序运行。页面加载,并为每个路由呈现,但没有(点击)工作或任何事件(仅 routerLink),也没有客户端控制台工作。以下是我的应用程序和服务器模块、应用程序和服务器电源、快速服务器和版本
app.module.ts
//libs
//import modules
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER, PLATFORM_ID, APP_ID, Inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ColorPickerModule } from 'ngx-color-picker';
import { SortablejsModule } from 'angular-sortablejs';
import { FroalaEditorModule, FroalaViewModule } from 'angular-froala-wysiwyg';
import { Ng2SearchPipeModule } from 'ng2-search-filter';
import { MomentModule } from 'angular2-moment';
import { FileUploadModule } from 'ng2-file-upload';
import { CustomFormsModule } from 'ng4-validators';
import { NgSelectModule } from '@ng-select/ng-select';
import { NgPipesModule } from 'angular-pipes';
import { NgxPaginationModule } from 'ngx-pagination';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CgBusyModule } from 'angular-busy2';
import { BrowserCookiesModule } from '@ngx-utils/cookies/browser';
//import { PrebootModule } from "preboot";
//TODO will be fixed shortly
//import { TextareaAutosizeModule } from 'ngx-textarea-autosize';
import { Location, CommonModule, isPlatformBrowser } from '@angular/common';
import * as _ from 'lodash';
//pipes
import { AppPipes } from './pipes/pipes';
//Directives
import { AppDirectives } from './directives/directives';
//Components
import { AppComponents, ModalComponents } from './components/components';
import { AppComponent } from'./app.component';
//Services
import { AppResources } from './components/resources';
import { AppServices } from './services/services';
import { AppLoadService } from '../config/app.load.service';
//HTTP Interceptors
import { APIInterceptor } from './services/api.interceptor';
const interceptors = [
{ provide: HTTP_INTERCEPTORS, useClass: APIInterceptor, multi: true }
];
//States
import { AppRoutingModule } from './app-routing.module';
export function init_app(appLoadService: AppLoadService) {
return () => appLoadService.load();
}
const IMPORTS = [
FormsModule,
CommonModule,
BrowserAnimationsModule,
HttpClientModule,
NgbModule.forRoot(),
ColorPickerModule,
SortablejsModule.forRoot({ animation: 150 }),
FroalaEditorModule.forRoot(),
FroalaViewModule.forRoot(),
Ng2SearchPipeModule,
MomentModule,
FileUploadModule,
AppRoutingModule,
CustomFormsModule,
NgSelectModule,
NgPipesModule,
NgxPaginationModule,
CgBusyModule,
BrowserCookiesModule.forRoot(),
BrowserTransferStateModule,
BrowserModule.withServerTransition({
appId: 'my-application'
}),
];
const DECLARATIONS = [
AppPipes,
AppDirectives,
AppComponents
];
const PROVIDERS = [
AppResources,
AppServices,
interceptors,
Location,
AppLoadService,
{ provide: APP_INITIALIZER, useFactory: init_app, deps: [AppLoadService], multi: true },
];
@NgModule({
declarations: DECLARATIONS,
imports: IMPORTS,
providers: PROVIDERS,
entryComponents: ModalComponents,
bootstrap: [AppComponent]
})
export class AppModule {
constructor(
@Inject(PLATFORM_ID) private platformId: Object,
@Inject(APP_ID) private appId: string
) {
const platform = isPlatformBrowser(platformId) ? 'in the browser' : 'on the server';
console.log(`Running ${platform} with appId=${appId}`);
}
}
app.server.module.ts
import { NgModule, } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { BrowserModule } from '@angular/platform-browser';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { ServerCookiesModule } from '@ngx-utils/cookies/server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
@NgModule({
imports: [
ServerTransferStateModule,
ModuleMapLoaderModule,
ServerCookiesModule.forRoot(),
AppModule,
ServerModule
],
bootstrap: [AppComponent]
})
export class AppServerModule { }
main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
document.addEventListener('DOMContentLoaded', () => {
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.log(err));
});
main.server.ts
export { AppServerModule } from './app/app.server.module';
快递-server.ts
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { renderModuleFactory } from '@angular/platform-server'
import { enableProdMode } from '@angular/core'
import * as express from 'express';
import * as cookieParser from 'cookie-parser';
import * as _ from 'lodash';
import { join } from 'path';
import { readFileSync } from 'fs';
import routes from 'main.routes';
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');
const dotenv = require('dotenv');
const compression = require('compression');
const fallback = require('express-history-api-fallback');
const git = require('git-rev');
const request = require('request');
function getMockMutationObserver() {
return class {
observe(node, options) {
}
disconnect() {
}
takeRecords() {
return [];
}
};
}
enableProdMode();
dotenv.config();
const PORT = process.env.PORT || 8080;
const DIST_FOLDER = join(process.cwd(), 'dist');
const app = express();
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html'), 'utf8');
const win = domino.createWindow(template);
global['window'] = win;
global['document'] = win.document;
global['DOMTokenList'] = win.DOMTokenList;
global['Node'] = win.Node;
global['Text'] = win.Text;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
global['MutationObserver'] = getMockMutationObserver();
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, AppServerModule } = require('main.server');
app.engine('html', (_, options, callback) => {
var fullUrl = options.req.protocol + '://' + options.req.get('host') + options.req.originalUrl;
let engine = ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP),
{
provide: 'REQUEST', useValue: (options.req)
},
{
provide: 'RESPONSE', useValue: (options.req.res)
},{
provide: 'serverUrl',
useValue: fullUrl
}
]
});
engine(_, options, callback);
});
app.set('view engine', 'html');
app.set('views', 'src')
app.use(cookieParser(process.env.COOKIE_TOKEN));
app.use(compression());
app.use('/images', express.static('images'));
app.use('/dist', express.static('dist'));
app.use(fallback('index.htm', { root: DIST_FOLDER }));
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
_.each(_.toPairs(routes.config), (route) => {
let method = route[0].split(' ')[0];
let url = route[0].split(' ')[1];
let func = routes[route[1]];
app[method](url, func);
})
app.get('/*', (req, res) => {
res.render('index', { req, res });
});
//SSL termination for heroku is handled by the load balancer
//but locally is handled by node
if(!process.env.APP_ENV || (process.env.APP_ENV === 'feature')){
var credentials = {
key: readFileSync(join(process.cwd(), 'ssl', 'certs', 'server', 'privkey.pem')),
cert: readFileSync(join(process.cwd(), 'ssl', 'certs', 'server', 'fullchain.pem')),
ca: readFileSync(join(process.cwd(), 'ssl', 'certs', 'ca', 'my-root-ca.crt.pem'))
};
var httpsServer = https.createServer(credentials, app);
httpsServer.listen(process.env.PORT || 8080, () => {
console.log('application started in dev mode');
});
} else {
var httpServer = http.createServer(app);
httpServer.listen(process.env.PORT || 8080, () => {
console.log('application started in prod mode');
});
}
版本
- Angular: 5.2.0
- 快递:4.15.0
- 打字稿:2.6.2
原来我所基于的通用启动器之一有错字,但结果是我将我的渲染视图指向了错误的目录。这就解释了为什么服务器端正在渲染,但没有客户端被启动,因为我正在渲染的 index.html 是浏览器 dist(因此没有包含任何包)。更改的具体行是
app.set('view engine', 'html');
app.set('views', 'src')
应该是
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'))
当运行我转换为服务器端呈现应用程序时,我的应用程序运行并呈现服务器端,但似乎没有客户端应用程序运行。页面加载,并为每个路由呈现,但没有(点击)工作或任何事件(仅 routerLink),也没有客户端控制台工作。以下是我的应用程序和服务器模块、应用程序和服务器电源、快速服务器和版本
app.module.ts
//libs
//import modules
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER, PLATFORM_ID, APP_ID, Inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ColorPickerModule } from 'ngx-color-picker';
import { SortablejsModule } from 'angular-sortablejs';
import { FroalaEditorModule, FroalaViewModule } from 'angular-froala-wysiwyg';
import { Ng2SearchPipeModule } from 'ng2-search-filter';
import { MomentModule } from 'angular2-moment';
import { FileUploadModule } from 'ng2-file-upload';
import { CustomFormsModule } from 'ng4-validators';
import { NgSelectModule } from '@ng-select/ng-select';
import { NgPipesModule } from 'angular-pipes';
import { NgxPaginationModule } from 'ngx-pagination';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CgBusyModule } from 'angular-busy2';
import { BrowserCookiesModule } from '@ngx-utils/cookies/browser';
//import { PrebootModule } from "preboot";
//TODO will be fixed shortly
//import { TextareaAutosizeModule } from 'ngx-textarea-autosize';
import { Location, CommonModule, isPlatformBrowser } from '@angular/common';
import * as _ from 'lodash';
//pipes
import { AppPipes } from './pipes/pipes';
//Directives
import { AppDirectives } from './directives/directives';
//Components
import { AppComponents, ModalComponents } from './components/components';
import { AppComponent } from'./app.component';
//Services
import { AppResources } from './components/resources';
import { AppServices } from './services/services';
import { AppLoadService } from '../config/app.load.service';
//HTTP Interceptors
import { APIInterceptor } from './services/api.interceptor';
const interceptors = [
{ provide: HTTP_INTERCEPTORS, useClass: APIInterceptor, multi: true }
];
//States
import { AppRoutingModule } from './app-routing.module';
export function init_app(appLoadService: AppLoadService) {
return () => appLoadService.load();
}
const IMPORTS = [
FormsModule,
CommonModule,
BrowserAnimationsModule,
HttpClientModule,
NgbModule.forRoot(),
ColorPickerModule,
SortablejsModule.forRoot({ animation: 150 }),
FroalaEditorModule.forRoot(),
FroalaViewModule.forRoot(),
Ng2SearchPipeModule,
MomentModule,
FileUploadModule,
AppRoutingModule,
CustomFormsModule,
NgSelectModule,
NgPipesModule,
NgxPaginationModule,
CgBusyModule,
BrowserCookiesModule.forRoot(),
BrowserTransferStateModule,
BrowserModule.withServerTransition({
appId: 'my-application'
}),
];
const DECLARATIONS = [
AppPipes,
AppDirectives,
AppComponents
];
const PROVIDERS = [
AppResources,
AppServices,
interceptors,
Location,
AppLoadService,
{ provide: APP_INITIALIZER, useFactory: init_app, deps: [AppLoadService], multi: true },
];
@NgModule({
declarations: DECLARATIONS,
imports: IMPORTS,
providers: PROVIDERS,
entryComponents: ModalComponents,
bootstrap: [AppComponent]
})
export class AppModule {
constructor(
@Inject(PLATFORM_ID) private platformId: Object,
@Inject(APP_ID) private appId: string
) {
const platform = isPlatformBrowser(platformId) ? 'in the browser' : 'on the server';
console.log(`Running ${platform} with appId=${appId}`);
}
}
app.server.module.ts
import { NgModule, } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { BrowserModule } from '@angular/platform-browser';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { ServerCookiesModule } from '@ngx-utils/cookies/server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
@NgModule({
imports: [
ServerTransferStateModule,
ModuleMapLoaderModule,
ServerCookiesModule.forRoot(),
AppModule,
ServerModule
],
bootstrap: [AppComponent]
})
export class AppServerModule { }
main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
document.addEventListener('DOMContentLoaded', () => {
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.log(err));
});
main.server.ts
export { AppServerModule } from './app/app.server.module';
快递-server.ts
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { renderModuleFactory } from '@angular/platform-server'
import { enableProdMode } from '@angular/core'
import * as express from 'express';
import * as cookieParser from 'cookie-parser';
import * as _ from 'lodash';
import { join } from 'path';
import { readFileSync } from 'fs';
import routes from 'main.routes';
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');
const dotenv = require('dotenv');
const compression = require('compression');
const fallback = require('express-history-api-fallback');
const git = require('git-rev');
const request = require('request');
function getMockMutationObserver() {
return class {
observe(node, options) {
}
disconnect() {
}
takeRecords() {
return [];
}
};
}
enableProdMode();
dotenv.config();
const PORT = process.env.PORT || 8080;
const DIST_FOLDER = join(process.cwd(), 'dist');
const app = express();
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html'), 'utf8');
const win = domino.createWindow(template);
global['window'] = win;
global['document'] = win.document;
global['DOMTokenList'] = win.DOMTokenList;
global['Node'] = win.Node;
global['Text'] = win.Text;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
global['MutationObserver'] = getMockMutationObserver();
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, AppServerModule } = require('main.server');
app.engine('html', (_, options, callback) => {
var fullUrl = options.req.protocol + '://' + options.req.get('host') + options.req.originalUrl;
let engine = ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP),
{
provide: 'REQUEST', useValue: (options.req)
},
{
provide: 'RESPONSE', useValue: (options.req.res)
},{
provide: 'serverUrl',
useValue: fullUrl
}
]
});
engine(_, options, callback);
});
app.set('view engine', 'html');
app.set('views', 'src')
app.use(cookieParser(process.env.COOKIE_TOKEN));
app.use(compression());
app.use('/images', express.static('images'));
app.use('/dist', express.static('dist'));
app.use(fallback('index.htm', { root: DIST_FOLDER }));
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
_.each(_.toPairs(routes.config), (route) => {
let method = route[0].split(' ')[0];
let url = route[0].split(' ')[1];
let func = routes[route[1]];
app[method](url, func);
})
app.get('/*', (req, res) => {
res.render('index', { req, res });
});
//SSL termination for heroku is handled by the load balancer
//but locally is handled by node
if(!process.env.APP_ENV || (process.env.APP_ENV === 'feature')){
var credentials = {
key: readFileSync(join(process.cwd(), 'ssl', 'certs', 'server', 'privkey.pem')),
cert: readFileSync(join(process.cwd(), 'ssl', 'certs', 'server', 'fullchain.pem')),
ca: readFileSync(join(process.cwd(), 'ssl', 'certs', 'ca', 'my-root-ca.crt.pem'))
};
var httpsServer = https.createServer(credentials, app);
httpsServer.listen(process.env.PORT || 8080, () => {
console.log('application started in dev mode');
});
} else {
var httpServer = http.createServer(app);
httpServer.listen(process.env.PORT || 8080, () => {
console.log('application started in prod mode');
});
}
版本
- Angular: 5.2.0
- 快递:4.15.0
- 打字稿:2.6.2
原来我所基于的通用启动器之一有错字,但结果是我将我的渲染视图指向了错误的目录。这就解释了为什么服务器端正在渲染,但没有客户端被启动,因为我正在渲染的 index.html 是浏览器 dist(因此没有包含任何包)。更改的具体行是
app.set('view engine', 'html');
app.set('views', 'src')
应该是
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'))