ngx-meta: dynamically added meta tags does not recognize facebook

I’m submitting a … (check one with “x”)

[ ] Support request => <!-- Please check the repository for a similar issue or PR before submitting -->

Current behavior

Expected/desired behavior

Minimal reproduction of the problem with instructions

import { MetaService } from '@ngx-meta/core';
import { FirstService } from './first.service';

export class FirstComponent {
      firstObj : any = {};

     constructor(private _metaService: MetaService,
                        private _firstService: FirstService) {}
      
     ngOnInit() {
             this._firstService.getQuestionOfTheWeek(0).subscribe(
                  res => firstObj = res,
                  error => this.errorGetRequest(error),
                  () => this.successGetRequest()
              );
       }

    successGetRequest(){
            this._metaService.setTag('og:description', this.firstObj .Desc);
            this._metaService.setTag('og:image', this.firstObj .ImageURL);
     }

}

What is the motivation / use case for changing the behavior?

Meta tag are getting added it is visible in the chrome developer tool but facebook does not recognize.

Environment

  • Angular version: 4.0.0
  • Browser:
  • Chrome (desktop) version XX
  • Chrome (Android) version XX
  • Chrome (iOS) version XX
  • Firefox version XX
  • Safari (desktop) version XX
  • Safari (iOS) version XX
  • IE version XX
  • Edge version XX
  • For Tooling issues:
  • Node version: XX <!-- run node --version -->
  • Platform:
  • Others:

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 1
  • Comments: 53

Most upvoted comments

@ishan123456789 @thecoconutstudio Somehow the emails got filtered out and I never observed these comments from both of you.

The below code is of course the most horrible hack, but it worked for me when no other solution on net was working. Till Angular team provides a fix, we decided to go with this.

Here’s my partial code.

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

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" />

@gianpaj I used server side rendering page but still not working. When I am doing view source on browser developer tool than meta tags are available. facebook Sharing Debugger doesn’t shows OG tags.

@gianpaj can you share your full code for meta update because am getting error in .subscribe((item: Item) => { can’t find name Item am also using same Repo from https://github.com/angular/universal-starter and ngx-meta

  1. if i update manually its work — this.meta.setTag('og:image', 'cAngulsadasdar 4 meta service');
  2. but when i update after API call its not reflected in view source(Cntrl + U) — this.meta.setTag('og:description', res[i].DocDesc);. any one help me out from this

This it’s mi PoC, I add validations for get the params, and I add a promise depending on params. I know this code can improve, but it’s a PoC

"use strict";

import "zone.js/dist/zone-node";

import * as express from "express";
import { join } from "path";

// SEO
const url = "YOUR_URL_API";
const url_img = url + "/img";
const api_url = url + "/api";

// Express server
const app = express();

// Http Request
const request = require("request-promise");

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), "dist/browser");

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {
  AppServerModuleNgFactory,
  LAZY_MODULE_MAP,
  ngExpressEngine,
  provideModuleMap
} = require("./dist/server/main");

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine(
  "html",
  ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [provideModuleMap(LAZY_MODULE_MAP)]
  })
);

app.set("view engine", "html");
app.set("views", DIST_FOLDER);

// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get(
  "*.*",
  express.static(DIST_FOLDER, {
    maxAge: "1y"
  })
);

// All regular routes use the Universal engine
app.get("*", (req, res) => {
  var seo: any = {
    title: "YOUR DEFAULT TITLE",
    description:
      "DEFAULT DESCRIPTION",
    image: "YOUR DEFAUL ROUTE IMAGE"
  };
  res.render("index", { req, res }, async (err, html) => {
    const params = req.params[0];
    if (params.indexOf("talento/") > -1) {
      let id = params.split("/");
      id = params[params.length - 1];
      id = Number(id);
      if (!isNaN(id) || Math.sign(id) > 0) {
        const talent = await getTalent(id);
        seo.title = talent.name;
        seo.description = strip_html_tags(talent.description);
        seo.image = `${url_img}/${talent.image}`;
      }
    }
    html = html.replace(/\$TITLE/g, seo.title);
    html = html.replace(/\$DESCRIPTION/g, strip_html_tags(seo.description));
    html = html.replace(/\$OG_DESCRIPTION/g, seo.description);
    html = html.replace(/\$OG_TITLE/g, seo.title);
    html = html.replace(/\$OG_IMAGE/g, seo.image);
    res.send(html);
  });
});

// Start up the Node server
app.listen(PORT, () => {
  console.log(`Node Express server listening on http://localhost:${PORT}`);
});

function strip_html_tags(str) {
  if (str === null || str === "") {
    return false;
  } else {
    str = str.toString();
    return str.replace(/<[^>]*>/g, "");
  }
}

async function getTalent(id): Promise<any> {
  try {
    const options = {
      uri: `${api_url}/services/${id}`,
      json: true
    };
    const res = await request(options)
      .then(repos => {
        return repos.response;
      })
      .catch(err => {
        return err;
      });
    return res;
  } catch (err) {
    return err;
  }
}


@gianpaj thanks for your kind replay. can you share your full code. so it’s helpful to build my app. because am also using same as your project. hlp me out my main issue is —(when i update after API call its not reflected in view source(Cntrl + U))

Depressing was a good word for it. Has anyone else found a working solution? If not, I’ll look into the @bharath-holla (I would need to hack it further for it to work for my needs) Thanks!

Reading through all the conversation, the result is not exactly clear. Here is what I think everything above means: to make this work with Facebook it is necessary to:

  • Use server-side rendering
  • arrange things such that routing waits for the data for the meta-tags
  • … and thus that the meta-tags end up already populated in the <head> of the statically served prerendered content

Is that correct?

my component class implements OnInit (see https://github.com/fulls1z3/ngx-meta/issues/118#issuecomment-344852631). I’ve never used or heard of CanActivate. Hopefully somebody can guide you in the right direction

I don’t think that’s true. I don’t know the underlying reasons why exactly it works, but my SSR server runs properly and renders the correct meta-tags in the HTML. View the source of this page for example: https://givebox.xyz/item/H1-ybixCZ

The og:image, og:url,og:title (and title) are dynamically generated.

I think you need to make sure you are running something like npm run build:ssr and npm run serve:ssr.

See the package.json https://github.com/angular/universal-starter/blob/master/package.json#L24

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

this is because (i believe) the components code are executed on the client side and not at server rendering.

Perhaps this could work? https://github.com/angular/universal#universal-gotchas