Ad

Update Meta Tags In Angular Universal With External API Call

I've spent more than 2 months but could not found a clear solution of how to proceed with angular universal. I've already spent about 6 months in implementing angular universal on a project for which I don't get a much time and now I'm stuck with this issue. Can anyone help me with this as it seems the whole world want to know a solution for Angular SSR.

Here's my code(Meta tag service):

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});
    }
    }
}

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

Here are some linked StackOverflow questions and GitHub issues:

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 tried to get help here from someone who successfully implemented but got no help)

https://github.com/maciejtreder/ng-toolkit/issues/460 (I opened it)

The list goes on I've seen a number of discussions which were never concluded. Anyone who can suggest how to make an API call before rendering in ng-express.

I have implemented SSR and use ngx-meta tag but still, fb crawler shows the default meta tags I used in the head tag.

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.

So is there a way to render meta tags using ng express engine.

Like this <title>{{meta.title}}</title>...

Ad

Answer

Couldn't find an easy way to do it but here's a temporary/hacky solution provided by a GitHub user here and I'm quoting his answer directly:

//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, res                         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);
            });
     }
  }
}

And in index.html, create template values as below:

    <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" />
Ad
source: stackoverflow.com
Ad