Angular2 - SEO - how to manipulate the meta description
Asked Answered
T

4

25

Search Results in google are displayed via TitleTag and the <meta name="description"..."/> Tag. The <title>-Tag is editiable via Angular2 how to change page title in angular2 router

What's left is the description.

Is it possibile to write a directive in angular2, that manipulates the meta-tags in the <head> part of my page.
So depending on the selected route, the meta description changes like:

<meta name="description" content="**my description for this route**"/>
Take answered 7/3, 2016 at 11:17 Comment(0)
B
39

Since Angular4, you can use Angular Meta service.

import { Meta } from '@angular/platform-browser';

// [...]

constructor(private meta: Meta) {}

// [...]

this.meta.addTag({ name: 'robots', content: 'noindex' });
Betseybetsy answered 21/4, 2017 at 8:23 Comment(5)
Will this help google index the title and meta tags correctly?Nitrobenzene
The tags added/updated via Meta doesn't get picked by the crawler, is there a way around to this?Oteliaotero
I imagine that this must be destined for use with Angular Universal.Pinhead
sorry, Actually I dont understand, inwhich file, I have to update this code.Mindymine
This works with Angular Universal. Here are the changes I made: github.com/kmturley/universal-starter/commit/…Heretofore
E
14

It is possible. I implemented it in my app and below I provide the description how it is made.

The basic idea is to use Meta from @angular/platform-browser

To dynamically change particular meta tag you have to:

  1. Remove the old one using removeTag(attrSelector: string) : void method.
  2. Add the new one using addTag(tag: MetaDefinition, forceCreation?: boolean) : HTMLMetaElement method.

And you have to do it when the router fires route change event.

Notice: In fact it is also necessary to have default <title>...</title> and <meta name="description"..." content="..."/> in head of index.html so before it is set dynamically there is already some static content.

My app-routing.module.ts content:

import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';

import { NgModule } from '@angular/core';
import { RouterModule, Routes, Router, NavigationEnd, ActivatedRoute } from '@angular/router';

import { StringComparisonComponent }  from '../module-string-comparison/string-comparison.component';
import { ClockCalculatorComponent }  from '../module-clock-calculator/clock-calculator.component';

import { Title, Meta } from '@angular/platform-browser';

const routes: Routes = [
  {
    path: '', redirectTo: '/string-comparison', pathMatch: 'full',
    data: { title: 'String comparison title', metaDescription: 'String comparison meta description content' }
  },
  {
    path: 'string-comparison',  component: StringComparisonComponent,
    data: { title: 'String comparison title', metaDescription: 'String comparison meta description content' }
  },
  {
    path: 'clock-time-calculator',  component: ClockCalculatorComponent,
    data: { title: 'Clock time calculator title', metaDescription: 'Clock time calculator meta description content' }
  }
];

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})

export class AppRoutingModule {

  constructor(
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private titleService: Title,
    private metaService: Meta
  ){
    //Boilerplate code to filter out only important router events and to pull out data object field from each route
    this.router.events
    .filter(event => event instanceof NavigationEnd)
    .map(() => this.activatedRoute)
    .map(route => {
        while (route.firstChild) route = route.firstChild;
        return route;
    })
    .filter(route => route.outlet === 'primary')
    //Data fields are merged so we can use them directly to take title and metaDescription for each route from them
    .mergeMap(route => route.data)
    //Real action starts there
    .subscribe((event) => {
        //Changing title
        this.titleService.setTitle(event['title']);

        //Changing meta with name="description"
        var tag = { name: 'description', content: event['metaDescription'] };
        let attributeSelector : string = 'name="description"';
        this.metaService.removeTag(attributeSelector);
        this.metaService.addTag(tag, false);
    });
  }

}
  1. As it can be seen there is an additional data object field for each route. It contains title and metaDescription strings which will be used as title and meta tag content.
  2. In constructor we filter out router events and we subscribe to filtered router event. Rxjs is used there, but in fact it is not necessary to use it. Regular if statements and loops could be used insead of stream, filter and map.
  3. We also merge our data object field with our event so we can easily use info like title and metaDescription strings.
  4. We dynamically change <title>...</title> and <meta name="description"..." content="..."/> tags.

Effects:

First component String comparison title and meta description tags

Second component Clock time calculator title and meta description tags

In fact I currently use a little bit more sophisticated version of this solution which uses also ngx-translate to show different title and meta description for different languages.
Full code is available in angular2-bootstrap-translate-website-starter project.
The app-routing.module.ts file with ngx-translate solution is exactly there: app-routing.module.ts.

There is also the production app which uses the same solution: http://www.online-utils.com.

For sure it is not the only way and there might be better ways to do it. But I tested this solution and it works.

In fact the solution is very similar to this from corresponding post about changing title: How to change page title in angular2 router.

Angular Meta docs: https://angular.io/docs/ts/latest/api/platform-browser/index/Meta-class.html. In fact they aren't very informative and I had to experiment and look into real .js code to make this dynamic meta change working.

Enzymolysis answered 1/6, 2017 at 20:1 Comment(1)
I tried this tags are getting added but on facebook on facebook open graph it doesn't shows. it there any way to work around ?Folia
B
6

I have developed and just released @ngx-meta/core plugin, which manipulates the meta tags at the route level, and allows setting the meta tags programmatically within the component constructor.

You can find detailed instructions at @ngx-meta/core github repository. Also, source files might be helpful to introduce a custom logic.

Batwing answered 11/11, 2016 at 8:44 Comment(10)
I have launched into live ng2-metadata and it works fine but when I get google to crawl my pages it still says I have duplicate meta tags. Is there anything else I need to do?Nitrobenzene
I think you should have been removed "static" tags from your html. ng2-metadata will write them on the fly.Batwing
Works perfectly for me. Thanks alot!Take
I found this doesnt work @RonaldPadur when lazy loaded routes are usedNitrobenzene
@Nitrobenzene lazy loaded routes are a big isssue in angular, especially when you compile the app in AoT using ngtools. Angular team is working on issues with it.Batwing
@BurakTasci I'm using your plugin to change title and my meta data for each of my routes. But the google bots wont show the title or meta description in google search. They use the default index.html pages title and meta tags for each route. Any way to fix this?Nitrobenzene
@Nitrobenzene you need server-side rendering in order that Google shows meta information that you set dynamically. I've done it using Angular Universal, explained it on this article and published a repo at github.com/ng-seed/universalBatwing
@BurakTasci my website is client side only and hosted with Firebase. So I cant use that. Any other suggestions?Nitrobenzene
@Nitrobenzene unfortunately this is not possible due to architectural limitations of SPA's. Same in React, Vue as well as in Angular. You need to run your Angular app on Express or IIS.Batwing
Angular Universal was exactly built to solve this problem.Batwing
I
3

There is currently no out-of-the-box solution only an open issue to implement it https://github.com/angular/angular/issues/7438.

You can of course implement something like the title service yourself, just use the TitleService as template

A Meta service similar to Title service is in the works (currently only a pull request).

Indomitable answered 7/3, 2016 at 11:45 Comment(4)
Thank you very much. I think I will go with the title service approachTake
Has this changed in the release version? I know there is a built in title service in platform-browser but is there anything for the description?Reconvert
Nope, nothing changed so far.Antipasto
This PR has now been merged and is part of 4.0.0-beta.0Sofia

© 2022 - 2024 — McMap. All rights reserved.