【问题标题】:Angular 9 SSR - unable to change dynamically the meta tagsAngular 9 SSR - 无法动态更改元标记
【发布时间】: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 文件中遇到错误,仅适用于我的游戏(对于所有其他页面,一切正常,并显示元数据)。

该错误实际上似乎发生在两种情况下:

  1. 按 Ctrl + U(很奇怪)

  2. 如果我刷新页面

这是错误:

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,对不起。日志是为了让您可以在显示页面时了解服务器端发生了什么
  • 看这里是否有帮助github.com/angular/angular/issues/36279
  • 很高兴知道它正在工作

标签: angular server-side-rendering angular-universal


【解决方案1】:

我遇到了模块 ngx-bar-rating 的问题。一旦我删除它,它就可以工作了!

【讨论】:

    猜你喜欢
    • 2020-07-04
    • 2017-10-12
    • 2022-01-21
    • 1970-01-01
    • 2020-01-04
    • 2011-11-10
    • 2011-10-09
    • 2019-08-30
    • 2020-09-16
    相关资源
    最近更新 更多