/meta

Dynamic page title & meta tags utility for Angular

Primary LanguageTypeScriptMIT LicenseMIT

@nglibs/meta Linux build Windows build coverage npm version

This repository holds the TypeScript source code and distributable bundle of @nglibs/meta, the dynamic page title & meta tags generator for Angular.

@nglibs/meta updates the page title and meta tags every time the route changes, based on Angular app's route configuration.

NOTICE

@nglibs/meta is the successor of ng2-metadata, and the current latest version number is v0.2.x. Releases with version number 1.X.x refer to ng2-metadata, and are being kept in order to maintain backwards compability - until Angular v4.0 (stable) gets released.

Table of contents:

Prerequisites

This package depends on @angular v2.0.0 but it's highly recommended that you are running at least @angular v2.4.0 and @angular/router v3.4.0. Older versions contain outdated dependencies, might produce errors.

Also, please ensure that you are using Typescript v2.1.6 or higher.

Getting started

Installation

You can install @nglibs/meta using npm

npm install @nglibs/meta --save

Examples

@nglibs packages

Adding @nglibs/meta to your project (SystemJS)

Add map for @nglibs/meta in your systemjs.config

'@nglibs/meta': 'node_modules/@nglibs/meta/bundles/meta.umd.min.js'

Route configuration

Add meta settings inside the data property of routes.

Note: meta properties such as title, description, author and publisher will be duplicated as og:title, og:description, og:author and og:publisher, so there's no need to declare them again in this context.

app.routes.ts

export const routes: Routes = [
  {
    path: 'home',
    component: HomeComponent,
    data: {
      meta: {
        title: 'Sweet home',
        description: 'Home, home sweet home... and what?'
      }
    }
  },
  {
    path: 'duck',
    component: DuckComponent,
    data: {
      meta: {
        title: 'Rubber duckie',
        description: 'Have you seen my rubber duckie?'
      }
    }
  },
  {
    path: 'toothpaste',
    component: ToothpasteComponent,
    data: {
      meta: {
        title: 'Toothpaste',
        override: true, // prevents appending/prepending the application name to the title attribute
        description: 'Eating toothpaste is considered to be too healthy!'
      }
    }
  }
  ...
];

app.module configuration

Import MetaModule using the mapping '@nglibs/meta' and append MetaModule.forRoot({...}) within the imports property of app.module (considering the app.module is the core module in Angular application).

app.module.ts

...
import { MetaModule } from '@nglibs/meta';
...

@NgModule({
  declarations: [
    AppComponent,
    ...
  ],
  imports: [
    ...
    RouterModule.forRoot(routes),
    MetaModule.forRoot()
  ],
  bootstrap: [AppComponent]
})

app.component configuration

Import MetaService using the mapping '@nglibs/meta' and inject it in the constructor of app.component (considering the app.component is the bootstrap component in Angular application).

app.component.ts

...
import { MetaService } from '@nglibs/meta';
...

@Component({
  ...
})
export class AppComponent {
  ...
  constructor(private readonly meta: MetaService) { }
  ...
}

Settings

You can call the forRoot static method using the MetaStaticLoader. By default, it is configured to prepend page titles after the application name (if any set). These default meta settings are used when a route doesn't contain any meta settings in its data property.

You can customize this behavior (and ofc other settings) by supplying meta settings to MetaStaticLoader.

The following example shows the use of an exported function (instead of an inline function) for AoT compilation.

Setting up MetaModule to use MetaStaticLoader

app.module.ts

...
import { MetaModule, MetaLoader, MetaStaticLoader, PageTitlePositioning } from '@nglibs/meta';
...

export function metaFactory(): MetaLoader {
  return new MetaStaticLoader({
    pageTitlePositioning: PageTitlePositioning.PrependPageTitle,
    pageTitleSeparator: ' - ',
    applicationName: 'Tour of (lazy/busy) heroes',
    defaults: {
      title: 'Mighty mighty mouse',
      description: 'Mighty Mouse is an animated superhero mouse character',
      'og:image': 'https://upload.wikimedia.org/wikipedia/commons/f/f8/superraton.jpg',
      'og:type': 'website',
      'og:locale': 'en_US',
      'og:locale:alternate': 'en_US,nl_NL,tr_TR'
    }
  });
}

...

@NgModule({
  declarations: [
    AppComponent,
    ...
  ],
  imports: [
    ...
    RouterModule.forRoot(routes),
    MetaModule.forRoot({
      provide: MetaLoader,
      useFactory: (metaFactory)
    })
  ],
  bootstrap: [AppComponent]
})

MetaStaticLoader has one parameter:

  • settings: MetaSettings : meta settings (by default, prepend page titles)

👍 Holy cow! @nglibs/meta will update the page title and meta tags every time the route changes.

Deferred initialization

You can delay the initialization of MetaService by setting the defer property of MetaStaticLoader to true. This will allow you to execute some tasks (retrieve data, etc.) before MetaService gets initialized.

When your tasks have been executed, simply invoke the init method to allow MetaService to update page titles and meta tags.

app.module.ts

...
import { MetaModule, MetaLoader, MetaStaticLoader, PageTitlePositioning } from '@nglibs/meta';
...

export function metaFactory(): MetaLoader {
  return new MetaStaticLoader({
    defer: true,
    pageTitlePositioning: PageTitlePositioning.PrependPageTitle,
    pageTitleSeparator: ' - ',
    applicationName: 'Tour of (lazy/busy) heroes',
    defaults: {
      title: 'Mighty mighty mouse',
      description: 'Mighty Mouse is an animated superhero mouse character',
      'og:image': 'https://upload.wikimedia.org/wikipedia/commons/f/f8/superraton.jpg',
      'og:type': 'website',
      'og:locale': 'en_US',
      'og:locale:alternate': 'en_US,nl_NL,tr_TR'
    }
  });
}

app.component.ts

...
import { MetaService } from '@nglibs/meta';
...

@Component({
  ...
})
export class AppComponent implements OnInit {
  ...
  constructor(private readonly meta: MetaService) { }

  ngOnInit(): void {
    someTask.subscribe((res: any) => {
      // some task done
      // some result collected
      if (!!res)
        // invoking the `init` method with false won't allow the use of meta service,
        // would be handy in the case you need to use meta service programmatically
        this.meta.init();
    });
  }
  ...
}

Using a callback function

The MetaStaticLoader accepts a callback function to use a custom logic on the meta tag contents (http-get, ngx-translate, etc.).

Return type of the callback function must be string or Observable<string>.

When a callback function is supplied, the MetaService will try to retrieve contents of meta tags (except og:locale and og:locale:alternate) using the specified callback. You can customize the behavior for missing/empty values, directly from the callback function itself.

app.module.ts

...
import { MetaModule, MetaLoader, MetaStaticLoader, PageTitlePositioning } from '@nglibs/meta';
import { TranslateService } from '@ngx-translate/core';
...

export function metaFactory(translate: TranslateService): MetaLoader {
  return new MetaStaticLoader({
    defer: true,
    callback: (key: string) => translate.get(key),
    pageTitlePositioning: PageTitlePositioning.PrependPageTitle,
    pageTitleSeparator: ' - ',
    applicationName: 'APP_NAME',
    defaults: {
      title: 'DEFAULT_TITLE',
      description: 'DEFAULT_DESC',
      'og:image': 'https://upload.wikimedia.org/wikipedia/commons/f/f8/superraton.jpg',
      'og:type': 'website',
      'og:locale': 'en_US',
      'og:locale:alternate': 'en_US,nl_NL,tr_TR'
    }
  });
}

...

@NgModule({
  declarations: [
    AppComponent,
    ...
  ],
  imports: [
    ...
    RouterModule.forRoot(routes),
    MetaModule.forRoot({
      provide: MetaLoader,
      useFactory: (metaFactory),
      deps: [TranslateService]
    })
  ],
  bootstrap: [AppComponent]
})

app.component.ts

...
import { MetaService } from '@nglibs/meta';
...

@Component({
  ...
})
export class AppComponent implements OnInit {
  ...
  constructor(private readonly translate: TranslateService,
              private readonly meta: MetaService) { }

  ngOnInit(): void {
    // add available languages & set default language
    this.translate.addLangs(['en', 'tr']);
    this.translate.setDefaultLang(defaultLanguage.code);

    this.meta.init();
    this.meta.setTag('og:locale', 'en-US');

    this.translate.use('en').subscribe(() => {
      // refresh meta tags
      this.meta.refresh();
    });
  }
  ...
}

home.routes.ts

import { Routes } from '@angular/router';
import { HomeComponent } from './home.component';

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    data: {
      meta: {
        title: 'PUBLIC.HOME.PAGE_TITLE',
        description: 'PUBLIC.HOME.META_DESC'
      }
    }
  }
];

You can find out in-depth examples about the use of callback function on @nglibs/example-app, which utilizes @nglibs utilities & showcasing common patterns and best practices.

Set meta tags programmatically

...
import { Component, OnInit } from '@angular/core';
import { MetaService } from '@nglibs/meta';
...

@Component({
  ...
})
export class ItemComponent implements OnInit {
  ...
  constructor(private readonly meta: MetaService) { }
  ...
  ngOnInit() {
    this.item = //HTTP GET for "item" in the repository
    this.meta.setTitle(`Page for ${this.item.name}`);
    this.meta.setTag('og:image', this.item.imageUrl);
  }
}

Credits

  • ng2-meta: Dynamic meta tags and SEO in Angular2

License

The MIT License (MIT)

Copyright (c) 2017 Burak Tasci