使用外部 API 调用更新 angular 通用中的元标记

Update meta tags in angular universal with external API call

我已经花了 2 个多月的时间,但找不到关于如何进行 angular 通用的明确解决方案。 我已经花了大约 6 个月的时间在一个我没有太多时间的项目上实现 angular universal,现在我遇到了这个问题。谁能帮我解决这个问题,因为似乎全世界都想知道 Angular SSR 的解决方案。

这是我的代码(元标记服务):

import {Injectable} from '@angular/core';
import { Meta, Title } from '@angular/platform-browser';
import {commonMetas} from './meta-data.model';

@Injectable()
export class SeoService {
    public commonMetas = commonMetas;
    constructor(public meta: Meta, public title: Title) {}

    setAutomatically (key = 'none') {
        const detail = this.commonMetas[key];
        /** setting defaults */
        this.setTitle(detail.title);
        this.setAuthor();
        this.setDescription(detail.description);
        this.setKeywords(detail.keyword);
    }
    setFromJson(key: {
        title: any,
        description: any,
        image: any,
        keyword: any,
        author: any
    }) {
        key.title = key.title ? key.title : this.commonMetas['none'].title;
        key.description = key.description ? key.description : this.commonMetas['none'].description;

    }
    setTitle(titleToSet = '') {
        this.title.setTitle(titleToSet);
    }
    setAuthor (nameToSet = '') {
        this.meta.addTag({ name: 'author',   content: 'havemybooks.com'});
    }
    setKeywords (keyword = '') {
        this.meta.addTag({ name: 'keywords', content: keyword});
    }
    }
}

还有我的component.ts

  ngOnInit() {
    this.sub = this.route.params.subscribe(params => {
      this.id = +params['id'];
      this.api.getParticular({id: this.id}).subscribe((response) => {
        this.content = response.blog_data[0];
        this.content.main_image = this.getImage(this.content.main_image);
        this.content.metaCreatedAt = moment(this.content.created_at).format('YYYY-MM-DD');
        this.content.displayCreatedAt = moment.utc(new Date(this.content.created_at)).fromNow();
        this.content.name = this.handleName(this.content.name);
        this.seo.setFromJson({
          title: this.content.title,
          image: this.content.main_image,
          description: this.content.blog,
          author: this.content.name,
          keyword: ''
        });
      });
   });
  }

这里有一些链接的 Whosebug 问题和 GitHub 问题:

Angular universal Server Side Rendering, Meta tags

Updating meta tags for SEO using angular universal

Angular Universal + External API

https://github.com/fulls1z3/ngx-meta/issues/101

Angular Universal - OG meta tags not working with child routes

https://github.com/fulls1z3/ngx-meta/issues/118(I 试图从成功实施但没有得到帮助的人那里获得帮助)

https://github.com/maciejtreder/ng-toolkit/issues/460(我打开了)

这个清单还在继续 我看到了很多从未结束的讨论。任何可以建议如何在 ng-express 中呈现之前进行 API 调用的人。

我已经实施了 SSR 并使用了 ngx-meta 标签,但 fb 爬虫仍然显示我在 head 标签中使用的默认元标签。

UPDATE: I'm able to get the source updated with view source option in chrome but the Facebook and Google crawler show the default meta tags which are there by default. It's very tough to launch my website with it remaining any help is appreciated. @Brandon guided me a lot I spent quite some time on implementing node with Jade and Nunchucks but since angular universal use angular-express by default so I wasn't able to use the above-mentioned render engines.

那么有没有办法使用 ng express 引擎呈现元标记。

像这样<title>{{meta.title}}</title>...

首先像这样在 index.html 文件中添加所有必需的元标记

然后更新每个必需组件中的元标记,使用 meta.updateTag() 如下

这个对我有用。

我尝试创建更新服务但没有成功,只有在同一组件中添加元更新时它才有效。

我在 Angular 2 Universal 上实现了设置标题。它应该使用渲染器来完成,像这样:

1.Import 组件内部的渲染器:

import {Renderer } from '@angular/core';

2.Add 构造函数内部的依赖关系:

constructor
    (
        ...
        private renderer: Renderer
    ) {}

3.Use 现在用于设置标题:

renderer.setText(renderer.selectRootElement('title'), value);

偶然发现这个寻找相同的解决方案。使用 setTimeout 对我有用。确保刷新缓存或使用隐身模式,否则您将看不到元数据(我犯了这个错误)。我的研究表明,在 SSR 呈现页面之前必须等待订阅 API 数据,因此 observable 必须保持打开状态(但我不是专家)。 https://angular.io/guide/observables。此外,使其工作的唯一方法是将订阅的响应直接放入元服务中。我将在我的代码中展示我的意思。

setTimeout( () =>
              this.seo.setFromJson({
              title: this.content.title,
              image: this.content.main_image,
              description: this.content.blog,
              author: this.content.name,
              keyword: ''
            });
    ,0);

我的代码和解决方案:

this.publicFeedService.getPublicProfile(data).subscribe((response:any) => {
        this.memberProfile = response[this.userId];
        setTimeout( () => 
        //Define the meta title using profile data
        this.titleService.setTitle('The best investment ideas from ' + response[this.userId].firstName + ' ' + response[this.userId].lastName), 0);
        setTimeout( () =>         
        //Define the meta tags using the user profile data
        this.metaService.addTags([
          {name: 'keywords', content: 'investing ideas, stock ideas, financial profile'},
          {name: 'description', content: response[this.userId].about},
          {name: 'robots', content: 'index, follow'}
        ]), 0);

找不到简单的方法,但这是 GitHub 用户 here 提供的 temporary/hacky 解决方案,我直接引用他的回答:

//all imports
enableProdMode();

export const app = express();

const mysql = require('mysql');
const httpRequest = require("request");
// all other consts

app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));
//all other piece of code in server.ts

app.get('/*', (req, res) => {
  res.render('index', {req, res}, (err, html) => {
    if (html) {
        
        //console.log(html);
        // This is where you get hold of HTML which "is about to be rendered"
    
        // after some conditional checks make a HTTP call
        let url = 'http://....';
        httpRequest.get(url, (error, response, body) => {
                        if(error) {
                            return console.log(error);
                        }
                        const respBody = JSON.parse(body);
                        if(respBody){
                              html = html.replace(/$TITLE/g, respBody.title);
                              html = html.replace(/$DESCRIPTION/g, respBody.description);
                              html = html.replace(/$OG_META_KEYWORDS/g, respBody.metaKeywords);
                              html = html.replace(/$OG_META_DESCRIPTION/g, respBody.metaDescription);
                              html = html.replace(/$OG_DESCRIPTION/g, respBody.ogDescription);
                              html = html.replace(/$OG_TITLE/g, respBody.ogTitle);
                              html = html.replace(/$OG_IMAGE/g, respBody.img);
                              html = html.replace(/$OG_SITE/g, respBody.ogSite);
                        }
                        res.send(html);
            });
     }
  }
}

并在 index.html 中创建如下模板值:

    <title>$TITLE</title>

    <meta name="description" content="$DESCRIPTION"/> 
    <meta name="keywords" content="$OG_META_KEYWORDS" />
    <meta name="metaDescription" content="$OG_META_DESCRIPTION"/> 
    <meta name="metaKeywords" content="$OG_META_KEYWORDS" />
    <meta property="og:title" content="$OG_TITLE" />
    <meta property="og:description" content="$OG_DESCRIPTION" />
    <meta property="og:site_name" content="$OG_SITE" /> 
    <meta property="og:type" content="website" />   
    <meta property="og:image" content="$OG_IMAGE" />