我最近开始将Angular 7.2.0应用程序转换为Angular通用应用程序。该应用程序已成功呈现服务器端,并在快速的PC和Internet连接上相当快速地传输了状态,但不幸的是,直到交出该页面后,呈现的页面仍缺少通常从API提取的任何数据。
我已将serverURL
添加到服务器提供的令牌中,并且在呈现服务器端时,所有http请求都是使用完全限定的url发出的。但是,这似乎无关紧要,因为所有http调用都在完成之前被取消!我的服务器未显示对api端点的调用,并且客户端记录了错误{}
(默认情况下显示为[Error]
)。
我正在使用TransferStateModule
和TransferHttpCacheModule
,但到目前为止没有任何效果。似乎快速引擎渲染将不会等待异步http调用。它会影响组件ngOnInit
中的http调用,服务constructor
中的resolver
中的http调用,因此我不知道如何获取数据。
对于诸如配置数据之类的东西,我将必要的字段作为令牌注入,并在服务器端渲染中检查该令牌,但是我无法对应用程序中的所有数据执行此操作。
下面是我的服务器和应用程序模块的相关部分
app.ts (节点快速服务器)
app.get('*.*', express.static(APP_CONFIG.client_root, {maxAge: 0}));
// Render Angular Universal
if (APP_CONFIG.universal) {
// Our index.html we'll use as our template
const templateFile = HelpersService.tryLoad(join(APP_CONFIG.client_root, './index.html'));
const config = configService.getConfig();
const template = templateFile.toString();
const win = domino.createWindow(template);
const fakeanimation = {
value: () => {
return {
enumerable: true,
configurable: true
};
},
};
global['window'] = win;
global['document'] = {...win.document,
createElement: () => {},
body: {
...win.document.body,
style: {
...win.document.body.style,
transform: fakeanimation,
opacity: fakeanimation,
bottom: fakeanimation,
left: fakeanimation,
}
}
};
global['navigator'] = win.navigator;
global['location'] = win.location;
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('../ssr/ssr.js');
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP),
]
}));
app.set('view engine', 'html');
app.set('views', APP_CONFIG.client_root);
app.get('*', (req, res, next) => {
const protocol = res.locals.protocol || req.protocol;
const showPort = ((APP_CONFIG.port === 80 && protocol === 'http') || (protocol === 'https')) ? false : true;
const serverUrl = `${protocol}://${req.hostname}${showPort ? ':'+APP_CONFIG.port : ''}`;
res.render('index', {
req,
document: template,
url: req.url,
providers: [
{
provide: 'serverURL',
useValue: serverUrl
},
{
provide: 'SERVER_CONFIG',
useValue: config || {}
},
{
provide: 'SERVER_AUTH',
useValue: {signedIn: !!res.locals.auth}
},
]
});
});
}
app.module.ts
@NgModule({
bootstrap: [
AppComponent
],
imports: [
BrowserModule.withServerTransition({appId: 'myapp'}),
BrowserAnimationsModule,
SharedModule,
TransferHttpCacheModule,
BrowserTransferStateModule,
RouterModule.forRoot(
...
app.server.module.ts
@NgModule({
imports: [
// The AppServerModule should import your AppModule followed
// by the ServerModule from @angular/platform-server.
AppModule,
ServerModule,
ServerTransferStateModule,
ModuleMapLoaderModule // <-- *Important* to have lazy-loaded routes work
],
// Since the bootstrapped component is not inherited from your
// imported AppModule, it needs to be repeated here.
bootstrap: [AppComponent],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: ServerSideRequestInterceptor,
multi: true,
},
]
})
export class AppServerModule {}
ServerSideRequestInterceptor
读取serverURL
注入令牌,并将其附加到以/
开头的所有请求中。
我感觉我已经很接近破解了,但是我无法告诉我如何让它等待应用程序中的http调用。
如果有帮助,我在这里添加了Repro应用:https://github.com/swimmadude66/ngUniversal-http-repro
答案 0 :(得分:0)
感谢@CaerusKaru在@nguniversal问题页面上解决了这一问题! https://github.com/angular/universal/issues/1046#issuecomment-455408250
从本质上讲,问题在于我使用interceptor
将完整的服务器路径附加到api调用中。它使用Object.assign(request, {url: fullServerUrl});
为所有以/
开头的http请求设置新的url。合适的方法显然是request.clone({url: fullServerUrl});
,并且更改了一个代码片段就导致数据像急流一样流动。
希望这对其他人有帮助,我将把repro-repo(很难说和输入)作为Angular7和Universal的样板。