Issue
I'm using Angular Universal and NestJS for my SSR. When I run npm run build:ssr
and deploy it I get no errors, but the meta tags inside of promises are not shown when linking the website, but links for routes where I set the meta tags at the root level of ngOnInit do load the meta tags as expected when linking the website.
Code for setting meta tags.
generateTags({ title = '', description = '', image = '' }) {
this.title.setTitle(title);
this.meta.addTags([
// Open Graph
{ name: 'og:url', content: `https://firestarter.fireship.io${this.router.url}` },
{ name: 'og:title', content: title },
{ name: 'og:description', content: description },
{ name: 'og:image', content: image },
// Twitter Card
{ name: 'twitter:card', content: 'summary' },
{ name: 'twitter:site', content: '@fireship_dev' },
]);
}
Eksample of code that does not load the meta tags
this.db.firestore.collection('users').doc(this.customerId).get().then((userRef) => {
this.seo.generateTags({
title: userRef.data().displayName,
description: userRef.data().about,
image: userRef.data().photoURL,
})
})
Example of code that does not load the meta tags
this.seo.generateTags({
title: userRef.data().displayName,
description: userRef.data().about,
image: userRef.data().photoURL,
})
Example with full component:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AngularFirestore } from '@angular/fire/firestore';
import { SeoService } from 'src/app/services/seo.service';
@Component({
selector: 'app-profileviewer',
templateUrl: './profileviewer.component.html',
styleUrls: ['./profileviewer.component.css']
})
export class ProfileviewerComponent implements OnInit {
customerId: string;
constructor(
private route: ActivatedRoute,
private db: AngularFirestore,
private seo: SeoService) { }
ngOnInit() {
this.customerId = this.route.snapshot.paramMap.get('id');
this.db.firestore.collection('users').doc(this.customerId).get().then((userRef) => {
this.seo.generateTags({
title: userRef.data().displayName,
description: userRef.data().about,
image: userRef.data().photoURL,
})
})
}
}
Who can I display content from e.g firebase in my meta tags with Angular9 with NestJS?
I have made a dummy project on GitHub where I get this error. To reproduce attach firebase project in the environments file and run npm run serve:ssr
(maybe npm run build:ssr
if you get an error) and see in the source code in chrome that the meta tags are not rendered.
EDIT: I have tried using resolve to fix this issue, but it still doesn't work with promises. This is the resolve script that I use:
import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Http } from '@angular/http';
@Injectable({
providedIn: 'root'
})
export class LoadSeoService implements Resolve<any> {
constructor (private http: Http, private db: AngularFirestore) {
}
resolve (route: ActivatedRouteSnapshot, rstate: RouterStateSnapshot) {
// Works
return "test"
// Does not work
// return this.db.firestore.collection("articles").doc(route.params['id'].substr(route.params['id'].length - 20)).get();
}
}
The code I use in my component:
this.route.data.subscribe(data => {
console.log(data.cres)
this.seo.generateTags({
title: data.cres,
description: data.cres,
image: data.cres,
})
});
Solution
Angular SSR waits until all the promises finish in ngInit() before it sends the response to the user. But I found that ngInit wasn't waiting using the firestore's get() method. So I tied with valueChanges() and it does work as expected and another good thing is that it returns an Observable with the data straight away, so your ngInit would look like:
ngOnInit() {
this.customerId = this.route.snapshot.paramMap.get('id');
this.db.collection('users').doc(this.customerId).valueChanges().subscribe(user => {
this.seo.generateTags({
title: user.displayName,
description: user.about,
image: user.photoURL,
});
});
}
Another thing you might find useful is to use TransferState to transfer what the server had resolved to the browser so it doesn't try to get the information once again when the browser fully loads the page. So your component would end up being something like:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AngularFirestore } from '@angular/fire/firestore';
import { SeoService } from 'src/app/services/seo.service';
import { TransferState, makeStateKey } from '@angular/platform-browser';
const USER_TAGS_KEY = makeStateKey('ProfileviewerComponent_UserTagsState');
@Component({
selector: 'app-profileviewer',
templateUrl: './profileviewer.component.html',
styleUrls: ['./profileviewer.component.css']
})
export class ProfileviewerComponent implements OnInit {
customerId: string;
tags = [];
constructor(
private route: ActivatedRoute,
private db: AngularFirestore,
private state: TransferState,
private seo: SeoService) { }
ngOnInit() {
this.customerId = this.route.snapshot.paramMap.get('id');
this.tags = this.state.get(USER_TAGS_KEY, null);
if (!this.tags) {
this.db.collection('users').doc(this.customerId).valueChanges().subscribe(user => {
this.tags = {
title: user.displayName,
description: user.about,
image: user.photoURL,
};
this.state.set(USER_TAGS_KEY, this.tags);
this.seo.generateTags(this.tags);
});
}
}
}
Answered By - Santi Barbat
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.