Dynamically add meta description based on route in Angular
First create a SEOService or Something like below:
import {Injectable} from '@angular/core';
import { Meta, Title } from '@angular/platform-browser';
@Injectable({
provideIn: 'root' // Add this to ensure your SEO service will be app-wide available
})
export class SEOService {
constructor(private title: Title, private meta: Meta) { }
updateTitle(title: string) {
this.title.setTitle(title);
}
updateOgUrl(url: string) {
this.meta.updateTag({ name: 'og:url', content: url })
}
updateDescription(desc: string) {
this.meta.updateTag({ name: 'description', content: desc })
}
}
After injecting the SEOService in your component (app.component.ts preferably), set meta tags and title in OnInit method
ngOnInit() {
this.router.events.pipe(
filter((event) => event instanceof NavigationEnd),
map(() => this.activatedRoute),
map((route) => {
while (route.firstChild) route = route.firstChild;
return route;
}),
filter((route) => route.outlet === 'primary'),
mergeMap((route) => route.data)
)
.subscribe((event) => {
this._seoService.updateTitle(event['title']);
this._seoService.updateOgUrl(event['ogUrl']);
//Updating Description tag dynamically with title
this._seoService.updateDescription(event['title'] + event['description'])
});
}
Then configure your routes like
{
path: 'about',
component: AboutComponent,
data: {
title: 'About',
description:'Description Meta Tag Content',
ogUrl: 'your og url'
}
},
IMHO this is a clear way of dealing with meta tags. You can update facebook and twitter specific tags easier.
Angular 6+ and RxJS 6+ solution for dynamically set title on route change
If/when you upgrade to Angular 6 this is the solution there.
This service will:
- Update meta title on route change.
- Option to override title for any reasons you want that.
Create/change your SEO/meta service to the following.
import { Injectable } from '@angular/core';
import { Title, Meta } from '@angular/platform-browser';
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
import { filter, map, mergeMap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class MetaService {
constructor(
private titleService: Title,
private meta: Meta,
private router: Router,
private activatedRoute: ActivatedRoute
) { }
updateMetaInfo(content, author, category) {
this.meta.updateTag({ name: 'description', content: content });
this.meta.updateTag({ name: 'author', content: author });
this.meta.updateTag({ name: 'keywords', content: category });
}
updateTitle(title?: string) {
if (!title) {
this.router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
map(() => this.activatedRoute),
map((route) => {
while (route.firstChild) { route = route.firstChild; }
return route;
}),
filter((route) => route.outlet === 'primary'),
mergeMap((route) => route.data)).subscribe((event) => {
this.titleService.setTitle(event['title'] + ' | Site name');
});
} else {
this.titleService.setTitle(title + ' | Site name');
}
}
}
Import your service and call it in the contructor.
app.component.ts
constructor(private meta: MetaService) {
this.meta.updateTitle();
}
And this still requires to format routes like this.
Route file.ts
{
path: 'about',
component: AboutComponent,
data: {
title: 'About',
description:'Description Meta Tag Content'
}
},
Hope this will help for you and other people looking to update the title/meta dynamically in Angular 6.
Title
and Meta
are providers that were introduced in Angular 4 and supposed to do this on both server and client side.
To create or update title
tag and description
meta tag, it is:
import { Meta, Title } from '@angular/platform-browser';
...
constructor(public meta: Meta, public title: Title, ...) { ... }
...
this.meta.updateTag({ name: 'description', content: description });
this.title.setTitle(title);
IMO using data (title, description etc) in the Routes array is not so consistent, we can aggregate all the data in one place. Also in 2021, we can use common Angular tools out of the box:
seo-sitemap.ts
export const seoSitemap: ISitemapTag[] = [
{ customUrl: '/contact', title: null, description: 'Some description there', image: '/assets/path/to/image' },
{ customUrl: '/about', title: 'custom about title', description: 'Some description about', image: '/assets/path/to/another-image' }
];
export interface ISitemapTag {
customUrl: string;
title: string | null;
description: string | null;
image: string | null;
}
metatags.service.ts:
import { Injectable } from '@angular/core';
import { Meta, MetaDefinition, Title } from '@angular/platform-browser';
@Injectable()
export class MetatagsService {
constructor(private title: Title, private meta: Meta) {}
updateTitle(title: string) {
this.title.setTitle(title);
}
updateTag(tag: MetaDefinition) {
this.meta.updateTag(tag);
}
updateTags(tags: Array<MetaDefinition | null>) {
tags.forEach((tag) => {
tag && this.meta.updateTag(tag);
});
}
}
app.component.ts
import { MetatagsService } from './shared/services/metatags.service';
import { seoSitemap } from './seoSitemap';
constructor() {
this.sub = this.router.events.subscribe((event: Event) => {
if (event instanceof NavigationEnd) {
this.setMetaTags(event);
}
});
}
ngOnDestroy() { this.sub && this.sub.unsubscribe() }
private setMetaTags(event: NavigationEnd) {
const item = seoSitemap.find((i) => event.urlAfterRedirects === i.customUrl);
if (item) {
if (item.title) this.metatagsService.updateTitle(item.title);
this.metatagsService.updateTags([
item.description ? { name: 'description', content: item.description } : null,
item.image ? { name: 'image', content: item.image } : null,
]);
this.metatagsService.updateTag({ property: 'og:url', content: window.location.href });
} else {
this.metatagsService.updateTitle('Common title there');
}
}