Open mistrynilesh opened 7 years ago
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
@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.
As mentioned in #101 , I used https://github.com/angular/universal-starter
How are you starting the server? I transpile/build it with npm run build:ssr
and then start the server.js in the dist folder with npm run serve:ssr
.
I have a very similar code:
export class ItemComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private itemsService: ItemsService, // my service
private router: Router,
private readonly meta: MetaService
) { }
ngOnInit() {
this.route.params.subscribe((data: any) => this.itemsService.getItem(data.id)
.subscribe((item: Item) => {
this.item = item;
this.meta.setTag('og:type', 'product');
this.meta.setTag('og:image', item.photo);
...
@gianpaj, Thanks.
I am also using default values to set OG tags-
export function metaFactory(): MetaLoader {
return new MetaStaticLoader({
pageTitlePositioning: PageTitlePositioning.PrependPageTitle,
pageTitleSeparator: ' - ',
applicationName: 'Tour of (lazy/busy) heroes',
defaults: {
'og:title': 'Mighty mighty mouse',
'og:description': 'Mighty Mouse is an animated superhero mouse character',
'og:image': 'https://d2y8liyagvllvg.cloudfront.net/images/website/nclexqow/20170801.png',
'og:type': 'website',
'og:locale': 'en_US',
'og:locale:alternate': 'en_US,nl_NL,tr_TR'
}
});
}
MetaModule.forRoot({
provide: MetaLoader,
useFactory: (metaFactory)
}),
"og:image" rendered properly but "og:description" and "og:title" doesn't works. not showing facebook debugger.
I still have the same problem aswell, just like I mentioned here: https://github.com/fulls1z3/ngx-meta/issues/101#issuecomment-337358565
I think the reason its working for @gianpaj is simply timing, the Facebook crawler has a really low timeout, and for very basic apps such as the universal-starter, the time it takes to load the item in ngOnInit is probably fast enough for it to work, but if the loading time is slightly longer, then the Facebook crawler will get a timeout before the item has been loaded.
What we need is a router guard that collects the needed data from the server before activating the route, which will force the Facebook crawler to wait for that.
However, the only way I know about to do this in ngx-meta is to use a backend service similar to the ngx-translate example provided. But, it seems that ngx-meta can only provide parameters with a fixed value to that backend service, from what I understand, not dynamic values like route ID's, etc.
If I'm wrong in that assumption then I would be very happy if someone can provide a howto on how to acheive what we need.
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
I am running a proper aot build with angular-cli. When and how do you collect the correct image url for the og:image tag? Because I can get the og:title to be set to what it should, but the og:image tag is simply not being set, despite the fact that I have created a router guard specifically for this that sets both the og:title and the og:image at the same time from the same data.
Edit: Some source code example on how you set the og:image tag from dynamic data would be welcome.
Edit2: I also want to clarify that I can get the og:image tag to work if I use the defaults, or if I specify a manual value, like in @mistrynilesh example. It is only when the image url is retrieved dynamically that it doesn't work. But the og:title does work, so it makes very little sense.
Edit3: Here is a small code example of a route guard that collects the data and sets the tags, before activating the route:
@Injectable()
export class ProductMetaGuard implements CanActivate {
constructor(private _platformLocation: PlatformLocation,
private _productsService: ProductsService,
private readonly meta: MetaService) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return Observable.create((observer: any) => {
let url: string = state.url;
let id: string = next.params['id'];
this._http
.get(environment.apiUrl + '/products/' + id)
.map((res: Response) => res.json())
.subscribe(
(val: Product) => {
let metaSettings: any = (next && next.data && next.data['meta'])
? next.data['meta']
: {};
if (val && val.title) {
metaSettings.title = val.title;
if (val.image && this._platformLocation && this._platformLocation['location']) {
metaSettings['og:image'] = this._platformLocation['location'].origin + '/images/products/' + val.image;
}
}
this.meta.update(url, metaSettings);
return observer.next(true);
},
(err: any) => {
return observer.error(err);
}
);
});
}
}
It sets the og:title properly, but it does not set the og:image properly. In the browser, the og:image is set, but not when I view source. But og:title is set on both.
I do believe it might be platformLocation that screws it up. However, how do I get the origin path during server rendering? I do not want to hardcode a path, since the origin is dynamic.
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
CanActivate
is used in the router, as a way to determine if a route is allowed or not, before the component is rendered.
As an update to the issue here, PlatformLocation
was the reason that the image does not show up for me.
So for now I'm gonna have to live with hardcoding the image paths and just retrieve the filename itself from the server.
I still wish there was a way to do this kind of thing using the ngx-meta router MetaGuard
, instead of having to either create my own custom router guard, or make calls in OnInit
.
As it is now, I can only use the ngx-meta MetaGuard
for non-dynamic content.
Any update on this issue? I'm also encountering the same issue @linusbrolin reported when the tags are dynamically retrieved from an API rather than being hard-coded. @linusbrolin did you end up resolving your issue?
@cahkee No, not really. I'm still hard-coding the paths and then use an API to retrieve the filenames. It's ugly, but works for now.
@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
this.meta.setTag('og:image', 'cAngulsadasdar 4 meta service');
this.meta.setTag('og:description', res[i].DocDesc);
. any one help me out from this@chozharajan my code is like https://github.com/fulls1z3/ngx-meta/issues/118#issuecomment-344852631
I don't understand the error you're mentioning: can't find name Item
.
Maybe open a StackOverflow question with the code, error message, or even better, a repo to reproduce, and the link to the SO question, and I'll try to help.
@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))
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:
<head>
of the statically served prerendered contentIs that correct?
@kylecordes If the content you want to use for the facebook meta-tags is dynamically collected via an api, based on the id and other variables you get from the route, then yes.
Hi, finally my project work well in local. but once deployed in server. i got view source (cntrl + U) but meta update not happening. i used npm run build:prerender cmd for build the project my reference Repo. and i placed dist/browser files in root directory. here is my project Link. so how to make meta update possible. NOTE: in local am getting meta update for npm run build:ssr
cmd. i think am wrongly doing the build cmd
. so how to deploy the universal project in IIS server. thanks in advance
Hi, how do you do to work well? do you use canactive to set meta before render??
@kylecordes, @linusbrolin, any update on this issue? have you been able to find an alternative? My issue is the same as the original problem explained in the ticket. As long as the static content is provided, it appears in facebook crawler. Any dynamic content coming from a http api never appears in the crawler.
@bharath-holla Yes, we had to do what I wrote above. Server-side render the pages so that Facebook can pick up the fields. In our case it is very slowly changing content so we did server side pre-rendering on a schedule. If your data is changing frequently,instead you would do server-side rendering on demand. There's plenty to read about angular Universal to make this happen, but the net result is you'll be serving HTML with all of the data already populated, which Facebook can parse to get its fields.
@kylecordes, Thanks, and even we found a similar solution yesterday. We modified server.ts to make a backend call and populated the index.html contents before issuing it to the renderer. And with that Facebook started to pickup the contents that renderer provided and tags started to appear in the FB Sharing Debugger. Thanks anyway.
HI, @bharath-holla can you please share the changes you made in server.ts as I'm stuck with this issue for quite while now.
@bharath-holla @ishan123456789 I'm also stuck on this, please are you able to share your solution. My meta tags are showing fine when viewing page-source, but not coming through via Facebook.
@thecoconutstudio not able to get it fixed for a while now. Really depressing problem.
@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" />
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!
I am facing this problem as well. Anyone got a better working solution for this?
@bharath-holla , can you please provide me with your source code?
@bharath-holla's solution is really the best I've managed to find after 3 days of searching and experimenting with different solutions. Thanks!
im still unable to implement it, can you provide will full server.ts imports ?
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;
}
}
@bharath-holla I changed my server.ts like your code but it cant update meta tags, view source doesnt shows new metatags value
@bharath-holla I changed my server.ts like your code but it cant update meta tags, view source doesnt shows new metatags value
Because, this is only for response in server-side
@MikeG96 I want that when I get responce from backend api it will change meta tags and show in "view source page", my app showed changed metatags in "inspect" view, but in "view source page" doesnt changed, and in facebook or other messangers shows link without any updated metatags.
Do you have any solution for shows updated meta tags on facebook or other messengers ?
Do you have any solution for shows updated meta tags on facebook or other messengers ?
This is an example of the "dirty solution" if you have your backend in JS https://github.com/fulls1z3/ngx-meta/issues/118#issuecomment-520235511
For Angular, you need update metas with a service, docs: https://angular.io/api/platform-browser/Meta
@MikeG96 My backend asp.net core 2.1, I can update my meta tags but when I open "view source page" from console it doesnt shows updated meta tags, and when i share my link in facebook also doesnt shows updated meta tags
Worked as expected @MikeG96 @bharath-holla, was out of nowhere but this small forum helped me solve the nightmare of 2 days. Just to help @Ilyoskhuja, make sure you have the correct route setup in your server.ts file and also check if there are multiple res.send() or res.render() blocks in your app.get(*).
Thanks with regards, Praveen Thirumurugan.
I already fixed, my problem was in prerendering.
I already fixed, my problem was in prerendering.
i have the same exact problem as yours. I can update the meta tags, shown in inspector, but nowhere to be seen in view source (ctrl+u) how did you solve this?
@rasyad You should check your prerendering code especially with backend part
Has anyone solved this issue ?, managing to publish a link on facebook linkedin and that these platforms recognize the og tags corresponding to each route?
Hello everyone, after doing more research and seeing that my "solution" mentioned above was not the best and much less was it in line with good practices, I finally solved the SEO issue with Angular SSR, I share my repository
Hello everyone, after doing more research and seeing that my "solution" mentioned above was not the best and much less was it in line with good practices, I finally solved the SEO issue with Angular SSR, I share my repository
Please, can you explain your solution? I'm already working with an Angular SSR project and a service to update meta tags, but crawlers don't recognize "in time" the changes.
Hello everyone, after doing more research and seeing that my "solution" mentioned above was not the best and much less was it in line with good practices, I finally solved the SEO issue with Angular SSR, I share my repository https://gitlab.com/mikeag96/angular-ssr
Please, can you explain your solution? I'm already working with an Angular SSR project and a service to update meta tags, but crawlers don't recognize "in time" the changes.
What are your doubts?
Hello everyone, after doing more research and seeing that my "solution" mentioned above was not the best and much less was it in line with good practices, I finally solved the SEO issue with Angular SSR, I share my repository
I tested using your solution and meta tags worked on Facebook and Twitter, but it looks like the standard meta tags didn't work. I tested using https://metatags.io
. Did you have this problem too?
Hello everyone, after doing more research and seeing that my "solution" mentioned above was not the best and much less was it in line with good practices, I finally solved the SEO issue with Angular SSR, I share my repository https://gitlab.com/mikeag96/angular-ssr
I tested using your solution and meta tags worked on Facebook and Twitter, but it looks like the standard meta tags didn't work. I tested using
https://metatags.io
. Did you have this problem too?
which are your "standard" tags?
Hello everyone, after doing more research and seeing that my "solution" mentioned above was not the best and much less was it in line with good practices, I finally solved the SEO issue with Angular SSR, I share my repository https://gitlab.com/mikeag96/angular-ssr
Please, can you explain your solution? I'm already working with an Angular SSR project and a service to update meta tags, but crawlers don't recognize "in time" the changes.
What are your doubts?
So the different in your changes was just
{ provide: 'NgxRequest', useValue: req, }, { provide: 'NgxResponse', useValue: res, },
in server.ts?
and to update the metatags, just use the meta service from Angular. rite?
I just added this { provide: 'NgxRequest', useValue: req, }, { provide: 'NgxResponse', useValue: res, }
in server.ts and updated the meta tags with Angular Meta service but it doesn't work.
The meta tags can be seen in Element but not in page view source.
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); }); }
} }
HI @bharath-holla ... How to get the route params here? Can you please provide inputs? I need to get the product details and render meta tags based on productId route param.
I'm submitting a ... (check one with "x")
Current behavior
Expected/desired behavior
Minimal reproduction of the problem with instructions
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: