【发布时间】:2020-05-21 15:55:01
【问题描述】:
我正在使用 Angular 9 SSR 应用程序,我正在尝试动态更新每个页面的元标记。
这是网站:https://gameswebapp-stage.herokuapp.com/
对于每个页面,除了游戏页面,我都可以更新元数据。
例如,挖掘机页面元数据没有变化 - https://gameswebapp-stage.herokuapp.com/game/digger
在GameComponent 我正在做以下事情:
NgOnInit 我正在打电话:(我已经硬编码了digger 游戏以使其更简单,但它仍然不起作用)
ngOnInit(): void {
const gameName = 'digger';
this.gameInfo = dic.GAMES_INFO[gameName];
console.log(`gameInfo`, this.gameInfo);
this.generalService.updateState(dic.STATES.game, this.gameInfo);
}
generalService 我正在这样做(请注意,所有页面都调用此代码,并且对他们来说它确实有效)
updateState(state: string, options: any) {
this.state = state;
// notify all subscribes that page is changed
this.stateSource.next(state);
this.updateMetaData(state, options);
if (isPlatformBrowser(this.platformId)) {
// auto scroll to top of page
setTimeout(() => {
const gameTitle = document.getElementById('headerContainer');
if (gameTitle) {
gameTitle.scrollIntoView({behavior: 'smooth'});
}
}, 300);
}
}
updateMetaData(state, options) {
const metaDataInfo = {
title: `${dic.CONSTANTS.appTitle} - `,
description: dic.CONSTANTS.metaData.desc,
keywords: dic.CONSTANTS.metaData.keywords,
// Facebook
url: `${dic.CONSTANTS.appUrL}${state}`,
image: `${dic.CONSTANTS.appUrL}assets/images/`
};
if (options && state === dic.STATES.game) {
metaDataInfo.title += options.name;
metaDataInfo.url += `/${options.fileName}`;
metaDataInfo.image += `${options.fileName}.png`;
metaDataInfo.description = options.desc;
metaDataInfo.keywords += `,${options.name}`;
}
else {
metaDataInfo.title += state;
metaDataInfo.image += `logo.png`;
}
console.log('metaDataInfo', metaDataInfo);
this.titleService.setTitle(metaDataInfo.title);
this.metaTagService.updateTag({ name: 'description', content: metaDataInfo.description });
this.metaTagService.updateTag({ name: 'keywords', content: metaDataInfo.keywords });
// Facebook
this.metaTagService.updateTag({ property: 'og:title', content: metaDataInfo.title });
this.metaTagService.updateTag({ property: 'og:url', content: metaDataInfo.url });
this.metaTagService.updateTag({ property: 'og:description', content: metaDataInfo.description });
this.metaTagService.updateTag({ property: 'og:image', content: metaDataInfo.image });
}
但不幸的是,index.html 中的元标记不会在游戏中发生变化(对于所有其他页面它确实会发生变化)。
这是我的server.ts 代码
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
const domino = require('domino');
const fs = require('fs');
const template = fs.readFileSync('dist/games/browser/index.html').toString();
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['localStorage'] = win.localStorage;
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import * as compression from 'compression';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
const distFolder = join(process.cwd(), 'dist/games/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule
}));
server.use(compression());
server.disable('x-powered-by');
server.set('view engine', 'html');
server.set('views', distFolder);
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log('Node Express server listening on http://localhost:', port);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
我在 server.ts 文件中遇到错误,仅适用于我的游戏(对于所有其他页面,一切正常,并显示元数据)。
该错误实际上似乎发生在两种情况下:
按 Ctrl + U(很奇怪)
如果我刷新页面
这是错误:
url: /game/digger
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'ɵcmp' of undefined
TypeError: Cannot read property 'ɵcmp' of undefined
at getComponentDef (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2078832)
at extractDirectiveDef (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2077372)
at Array.map (<anonymous>)
at def.directiveDefs (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2076998)
at createTView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2136884)
at getOrCreateTComponentView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2135760)
at createRootComponentView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2263977)
at ComponentFactory$1.create (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2333950)
at R3ViewContainerRef.createComponent (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2167016)
at router_RouterOutlet.activateWith (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:3550373)
at resolvePromise (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1926319)
at resolvePromise (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1925296)
at C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1928123
at ZoneDelegate.invokeTask (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1917423)
at Object.onInvokeTask (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2412917)
at ZoneDelegate.invokeTask (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1917313)
at Zone.runTask (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1909988)
at drainMicroTaskQueue (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1921031)
at ZoneTask.invokeTask (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1919072)
at Server.ZoneTask.options.useG.invoke (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:1918837) {
rejection: TypeError: Cannot read property 'ɵcmp' of undefined
at getComponentDef (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2078832)
at extractDirectiveDef (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2077372)
at Array.map (<anonymous>)
at def.directiveDefs (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2076998)
at createTView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2136884)
at getOrCreateTComponentView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2135760)
at createRootComponentView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2263977)
at ComponentFactory$1.create (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2333950)
at R3ViewContainerRef.createComponent (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2167016)
at router_RouterOutlet.activateWith (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:3550373),
promise: ZoneAwarePromise [Promise] {
__zone_symbol__state: 0,
__zone_symbol__value: TypeError: Cannot read property 'ɵcmp' of undefined
at getComponentDef (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2078832)
at extractDirectiveDef (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2077372)
at Array.map (<anonymous>)
at def.directiveDefs (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2076998)
at createTView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2136884)
at getOrCreateTComponentView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2135760)
at createRootComponentView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2263977)
at ComponentFactory$1.create (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2333950)
at R3ViewContainerRef.createComponent (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2167016)
at router_RouterOutlet.activateWith (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:3550373)
},
zone: Zone {
_parent: Zone {
_parent: null,
_name: '<root>',
_properties: {},
_zoneDelegate: [ZoneDelegate]
},
_name: 'angular',
_properties: { isAngularZone: true, maybeDelayChangeDetection: false },
_zoneDelegate: ZoneDelegate {
_taskCounts: [Object],
zone: [Circular],
_parentDelegate: [ZoneDelegate],
_forkZS: null,
_forkDlgt: null,
_forkCurrZone: null,
_interceptZS: null,
_interceptDlgt: null,
_interceptCurrZone: null,
_invokeZS: [Object],
_invokeDlgt: [ZoneDelegate],
_invokeCurrZone: [Circular],
_handleErrorZS: [Object],
_handleErrorDlgt: [ZoneDelegate],
_handleErrorCurrZone: [Circular],
_scheduleTaskZS: [Object],
_scheduleTaskDlgt: [ZoneDelegate],
_scheduleTaskCurrZone: [Circular],
_invokeTaskZS: [Object],
_invokeTaskDlgt: [ZoneDelegate],
_invokeTaskCurrZone: [Circular],
_cancelTaskZS: [Object],
_cancelTaskDlgt: [ZoneDelegate],
_cancelTaskCurrZone: [Circular],
_hasTaskZS: [Object],
_hasTaskDlgt: [ZoneDelegate],
_hasTaskDlgtOwner: [Circular],
_hasTaskCurrZone: [Circular]
}
},
task: ZoneTask {
_zone: Zone {
_parent: [Zone],
_name: 'angular',
_properties: [Object],
_zoneDelegate: [ZoneDelegate]
},
runCount: 0,
_zoneDelegates: null,
_state: 'notScheduled',
type: 'microTask',
source: 'Promise.then',
data: ZoneAwarePromise [Promise] {
__zone_symbol__state: 0,
__zone_symbol__value: TypeError: Cannot read property 'ɵcmp' of undefined
at getComponentDef (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2078832)
at extractDirectiveDef (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2077372)
at Array.map (<anonymous>)
at def.directiveDefs (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2076998)
at createTView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2136884)
at getOrCreateTComponentView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2135760)
at createRootComponentView (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2263977)
at ComponentFactory$1.create (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2333950)
at R3ViewContainerRef.createComponent (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:2167016)
at router_RouterOutlet.activateWith (C:\Users\Yoni\WebstormProjects\games\dist\games\server\main.js:1:3550373)
},
scheduleFn: undefined,
cancelFn: undefined,
callback: [Function],
invoke: [Function]
}
【问题讨论】:
-
不,我的意思是在您自己的应用程序代码中。例如,在您的
GameComponent中的ngOnInit中,当检索到您的数据时,导航完成时......我可能无法仅通过查看代码来提供帮助,所以不妨先尝试添加日志。 -
如果您的 index.html 中已经存在元数据,您绝对应该使用
updateMeta。此外,setTimeouts 应该只在客户端执行(使用isPlatformBrowser) -
是的,我的意思是
updateTag,对不起。日志是为了让您可以在显示页面时了解服务器端发生了什么 -
很高兴知道它正在工作
标签: angular server-side-rendering angular-universal