skip to Main Content

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

3

Answers


  1. Your call to firestore is async so SSR finishes before the promise resolves. All async functions need to be resolved on router level so angular waits for them to be finished.

    Try adding the firestore request to a route resolver and use the data from resolver inside the ngOnInit() function.

    see more infos here: https://angular.io/api/router/Resolve

    Login or Signup to reply.
  2. Finally, after lot of research and debugging i found out why were you not seeing meta tags in your index.html.

    1. Be sure your meta tags are getting added. I printed meta object in console After adding Tags. You can also check it. Print it :-
      in output go under _doc>documentChild>innerHtml

    enter image description here

    In the image above in innerHTML, you can notice your meta tags.

    1. Now why they were not part of index.html, when it was displayed is, because firebase promise will automatically get resolved when its connected to firebase and receive the data. But because its part of your. Your ssr will not generate tags. These tags will be appened later dynamically when your proise is resolved.Any thing that is being changed after page loads that won’t be shown in page view source.If you want to check the updated meta tags you need to inspect element and check there.

    To prove my 2nd point, here is screenshot of file in network tab of index.html.

    enter image description here

    and there is screenshot of element inspector showing that your meta tags were added successfully but on runtime.

    enter image description here

    If you will create a normal promise and inside that call your generate tags method, it will be showing in source itself, because you will resolve that promise there only. But in case of firebase, that promise is not resolved until data is fetched, hence your ssr coun’t pre-render it.

    To enable firebase with ssr, you can find info here :-

    https://medium.com/@antonybudianto/server-side-rendering-with-react-and-firebase-functions-cd67fdb2b605

    Login or Signup to reply.
  3. 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);
          });
        }
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search