使用外部 API 调用更新 Angular Universal 中的元标记

Posted

技术标签:

【中文标题】使用外部 API 调用更新 Angular Universal 中的元标记【英文标题】:Update meta tags in angular universal with external API call 【发布时间】:2019-03-16 20:30:18 【问题描述】:

我已经花了 2 个多月的时间,但找不到一个明确的解决方案来说明如何继续使用 Angular Universal。 我已经花了大约 6 个月的时间在一个我没有太多时间的项目上实现 angular Universal,现在我被这个问题困住了。任何人都可以帮我解决这个问题,因为似乎全世界都想知道 Angular s-s-r 的解决方案。

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

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: ''
        );
      );
   );
  

以下是一些链接的 *** 问题和 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 调用。

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

更新:我可以通过查看源代码选项更新源代码 chrome 但 Facebook 和 Google 爬虫显示默认元标记 默认情况下。启动我的网站非常困难 感谢您提供任何帮助。 @Brandon 指导了我很多我花了 用 Jade 和 Nunchucks 实现节点需要相当长的时间,但因为 angular 通用默认使用 angular-express,所以我无法 使用上述渲染引擎。

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

赞这个<title>meta.title</title>...

【问题讨论】:

这个问题似乎离题了,因为它不在帮助中心所述的讨论范围内。 你能告诉我这个问题是如何偏离主题的,而我链接的问题不是。 AFAIK 如果另一个人没有接受的答案,则允许提出相同的问题。 @Will这就是为什么您不能将我的问题标记为重复的原因。那么这是怎么回事。如果需要,我会编辑,但请告诉我 在通过 Angular s-s-r 提供​​ index.html 时,您需要调用 API 来获取元标记信息吗? 是的,当我在隐身窗口中看到页面源时,元标记会在查看源页面中更新,但是当我尝试分享到 fb 时,它没有更新 @Brandon 明白了。您是否在初始渲染后修改元标记? 【参考方案1】:

找不到简单的方法,但这是 GitHub 用户 here 提供的临时/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" />

【讨论】:

【参考方案2】:

偶然发现这个寻找相同的解决方案。使用 setTimeout 对我有用。确保刷新缓存或使用隐身模式,否则您将看不到元数据(我犯了这个错误)。我的研究表明,在 s-s-r 呈现页面之前必须等待订阅 API 数据,因此可观察性必须保持打开状态(但我不是专家)。 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);

【讨论】:

我不认为 setTimeout 等待渲染,所以它不应该修复它。不管怎样,我会试一试,虽然它似乎没有帮助。 我同意...相信我,我也对此感到沮丧,我认为我的回答可能会意外解决它。然而,我今天查了一下,Facebook 正在获取 API 设置的元数据。 您使用的 Angular 版本可能已在最新版本中修复。我在 v6 中问过这个问题。 只是跟进。我现在有多个页面可以使用用于设置元标记的 API 数据。我还能够进行两次不同的 API 调用和 addTags 两次,它仍然会更新页面源。真的很好奇如果我遗漏了什么,您可能会遇到什么问题 我还没有更新我的 Angular 版本,在 v6 之后将尝试最新版本。我正在等待 IONIC 发布 v5 来重组我的应用程序,然后与它们一起使用 s-s-r。您能否为此制作一个演示 github 存储库,以便我可以快速测试。【参考方案3】:

我在 Angular 2 Universal 上实现了设置标题。应该使用 Renderer 来完成,如下所示:

1.在组件内部导入渲染器:

import Renderer  from '@angular/core';

2.在构造函数内部添加依赖:

constructor
    (
        ...
        private renderer: Renderer
    ) 

3.现在用它来设置标题:

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

【讨论】:

【参考方案4】:

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

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

这个对我有用。

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

【讨论】:

现在按 ctrl+u 查看页面源代码。您和爬虫都不会看到更新后的元标记。 问题在于 API 调用需要时间来更新,但爬虫不会等待响应 请将代码和数据添加为文本 (using code formatting),而不是图像。图片:A)不允许我们复制粘贴代码/错误/数据进行测试; B) 不允许根据代码/错误/数据内容进行搜索;和many more reasons。一般来说,文本格式的代码/错误/数据>>>>作为图像的代码/错误/数据>>没有。除了代码格式的文本之外,只有在图像添加了一些重要的东西,而不仅仅是文本代码/错误/数据传达的内容时,才应该使用图像。

以上是关于使用外部 API 调用更新 Angular Universal 中的元标记的主要内容,如果未能解决你的问题,请参考以下文章

API调用后Angular应用程序不更新列表

在Angular中使用websocket发布api调用不起作用

如何在使用 AngularJS/Web API/Angular-ui-router 的指令中调用 $http.put 服务后更新页面

Angular 5:无法使用异步管道更新模板

Angular5服务器端渲染,外部Api数据服务在s-s-r中不起作用

在angular 5组件中调用外部js文件的函数