inthevortex
inthevortex

Reputation: 334

Title and Meta tags not rendering in angular SSR in server

I have developed a website using Angular 6 in the frontend. By default Angular is not SEO friendly, so in order to do that, I implemented it in the way of Angular-Universal or Angular SSR (Server Side Rendering). I updated the code and comparing the page sources from before and now, I can see my application inside the tags <app-root> and </app-root>, before only "loading..." would come.

I am using the MetaService and TitleService from @angular/platform-browser to update the desired <meta> tags for Facebook and Twitter and the <title> tag respectively.

The problem is when I run the node server in my local system, view-source shows me the rendered meta tags, but when I run the same code in node server on AWS VM, I don't get the rendered meta tags, but other application code is available.

UPDATE: The function that adds the meta tags

updateMetaTags(egElement: Elements[]) {
    this.url = 'https://example.com/eg/' + this.id;
    const title = egElement[1].innerHTML;
    this.tweetText = 'Check the latest blog on \"' + title + '\"';
    this.meta.addTags([
      { property: 'og:url', content: this.url },
      { property: 'og:type', content: 'website' },
      { property: 'og:title', content: title },
      { property: 'og:description', content: 'Author: ' + egElement[2].innerHTML },
      { property: 'og:image', content: this.egElement[3].img }
    ]);
  }

I call this function in ngOnInit(). It does the rendering properly in my local machine, but does not do it on the server.

egElement and id is returned from the service call to backend and meta service has been imported and injected in the constructor.

Upvotes: 8

Views: 9821

Answers (3)

C Crk
C Crk

Reputation: 1

Basically Server.ts file is responsible for perform server-side rendering. The flow is like first Server.ts hits the url which we are searching then the result will be rendered and again browser request will hit the url. If we are using any localstorage , session storage, windows in the code level this will not be accessed by server, so the flow will be disconnected and initial static data in the page only rendered.We can control browser request if already server rendered the page by using State Transfer Key Mechanism.

Upvotes: 0

Stefan Morcodeanu
Stefan Morcodeanu

Reputation: 2158

Hi I also was facing this error so make sure in your server.ts file to have imported import 'reflect-metadata'; to reflect all meta to index.html

You can take a look at my server.ts config file\

import 'zone.js/dist/zone-node';
import 'reflect-metadata';

import { enableProdMode } from '@angular/core';
// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

import * as express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

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

const template = readFileSync(join(DIST_FOLDER, 'index.html')).toString();

const domino = require('domino');
const win = domino.createWindow(template);
global['localStorage'] = win.localStorage;
global['window'] = win;
global['document'] = win.document;
global['Document'] = win.document;
global['DOMTokenList'] = win.DOMTokenList;
global['Node'] = win.Node;
global['Text'] = win.Text;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
global['MutationObserver'] = getMockMutationObserver();

function getMockMutationObserver() {
  return class {
    observe(node, options) {}

    disconnect() {}

    takeRecords() {
      return [];
    }
  };
}

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = 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) => {
  res.render('index', { req });
});

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

Upvotes: 2

Drenai
Drenai

Reputation: 12397

If you're using custom XHR calls , e.g. not using Angular HttpClient, the SSR won't wait for API call responses (this can also occur if using 3rd party libraries to retrieve API data). Looking at your site there is no server side rendering occurring other than the page layout/header/footer

I'm guessing it's related to the API data not being retrieved in SSR. Maybe you could update your question with some info on this?

There is a well tested and maintained library called ngx-meta that's universal (SSR) compatible. You could look at their implementation, and demos, or give their library a go https://github.com/fulls1z3/ngx-meta

Upvotes: 4

Related Questions