Динамически добавлять мета-описание на основе маршрута в Angular

Я использую Angular 5 для создания небольшого веб-сайта типа брошюры. Пока что у меня настроены маршруты, и заголовок страницы динамически меняется в зависимости от активированного маршрута. Я получил эту работу, используя инструкции в этом блоге: https://toddmotto.com/dynamic-page-titles-angular-2-router-events

В настоящее время я храню свои маршруты и заголовки в app.module.ts как таковой:

imports: [
    BrowserModule,
    RouterModule.forRoot([
      { 
        path: '', 
        component: HomeComponent,
        data: {
          title: 'Home'
        }
      },
      { 
        path: 'about', 
        component: AboutComponent,
        data: {
          title: 'About'
        } 
      },
      { 
        path: 'products-and-services', 
        component: ProductsServicesComponent,
        data: {
          title: 'Products & Services'
        }  
      },
      { 
        path: 'world-class-laundry', 
        component: LaundryComponent,
        data: {
          title: 'World Class Laundry'
        }  
      },
      { 
        path: 'contact', 
        component: ContactComponent,
        data: {
          title: 'Contact'
        }  
      },
      { 
        path: '**', 
        component: NotFoundComponent,
        data: {
          title: 'Page Not Found'
        }  
      }
    ])
  ],

Я хотел бы также хранить там свои метаописания, если добавить их в data: это будет достаточно просто.

Я извлекаю данные заголовка с помощью следующего кода, который указан в ссылке на блог выше:

ngOnInit() {
    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')
      .mergeMap((route) => route.data)
      .subscribe((event) => {
        this.titleService.setTitle(event['title']);
      });
  }

Итак, мой вопрос, есть ли способ динамически установить мета-описание, используя тот же метод? Если есть способ объединить заголовок страницы и функцию мета-описания, это было бы идеально.

У меня очень ограниченная угловая тренировка, так что это может быть нубийским вопросом. Я больше похож на парня дизайнер /CSS/HTML.

Ответ 1

Сначала создайте SEOService или что-то вроде ниже:

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

@Injectable()
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 })
  }

После внедрения SEOService в ваш компонент установите метатеги и заголовок в методе OnInit.

ngOnInit() {
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')
  .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'])
  }); 
}

Изменение: Для RxJs 6+, который использует оператор трубы

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'])
      }); 
    }

Затем настройте свои маршруты как

      { 
        path: 'about', 
        component: AboutComponent,
        data: {
          title: 'About',
          description:'Description Meta Tag Content',
          ogUrl: 'your og url'
        } 
      },

ИМХО, это четкий способ работы с мета-тегами. Вы можете легко обновить теги для Facebook и Twitter.

Ответ 2

Title и Meta - провайдеры, которые были введены в Angular 4 и должны делать это как на стороне сервера, так и на стороне клиента.

Чтобы создать или обновить тег title и метатег description, это:

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

Ответ 3

Угловое решение 6+ и RxJS 6+ для динамически устанавливаемого заголовка при изменении маршрута

Если/когда вы обновитесь до Angular 6, это решение там.

Эта услуга будет:

  • Обновить мета-заголовок при изменении маршрута.
  • Возможность переопределить заголовок по любым причинам, которые вы хотите.

Создайте/измените ваш SEO/мета сервис следующим образом.

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

Импортируйте ваш сервис и вызовите его в конструкторе.

app.component.ts

constructor(private meta: MetaService) {
    this.meta.updateTitle();
}

И это все еще требует форматировать маршруты, как это.

Маршрут file.ts

{ 
    path: 'about', 
    component: AboutComponent,
    data: {
      title: 'About',
      description:'Description Meta Tag Content'
    } 
  },

Надеюсь, это поможет вам и другим людям, которые хотят динамически обновлять заголовок/мета в Angular 6.

Ответ 4

Вы можете сделать следующее из Angular (на самом деле для решения требуется только jQuery, а не что-то особенное из Angular)

В конфигурации маршрутизатора:

  { 
    path: 'about', 
    component: AboutComponent,
    data: {
      title: 'About',
      description: 'Here some description' 
    }
  },

В вашем компоненте:

  ...
  .subscribe((event) => {
      this.titleService.setTitle(event['title']);
      $('meta[name=description]').attr('content', event['description']);
  });

Если вы не хотите использовать jQuery, есть простые функции JavaScript для поиска мета-элементов и изменения их атрибутов (getElementsByTagName и getAttribute). Полный код для альтернативы JavaScript можно найти в этом другом SO-вопросе здесь.

Примечание.. Я также хотел бы сохранить заголовок и описание как частные поля в самих компонентах, а не в конфигурации маршрутизатора. Это позволяет избежать необходимости подписываться на что угодно и оставляет в настройках маршрутизатора только связанные с маршрутизатором вещи.

Ответ 5

Вот соответствующие части из моего проекта. (Угловой 2/4)


приложение-routing.module.ts:

Маршруты:

... const appRoutes: Routes = [
    {
        path: 'path1', loadChildren: './path1#path1Module',
        data: {
            title: '...',
            description: '...',
            keywords: '...'
        }
    },
    {
        path: 'path2', loadChildren: './path2#path2Module',
        data: {
            title: '...',
            description: '...',
            keywords: '...'
        }
    } ...

app.component.ts (или ваш компонент начальной загрузки):

импорт:

// imports
import { Component, OnInit} from '@angular/core';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import { Title,Meta } from '@angular/platform-browser';

конструктор:

    // constructor:
        constructor(private router: Router,
                    private route: ActivatedRoute,
                    private titleService: Title, private meta: Meta) {}

Метод ngOnInit():

ngOnInit() {

        this.router.events
            .filter((event) => event instanceof NavigationEnd)
            .map(() => this.route)
            .map((route) => {
                while (route.firstChild) route = route.firstChild;
                return route;
            })
            .filter((route) => route.outlet === 'primary')
            .mergeMap((route) => route.data)
            .subscribe((event) => {
                this.updateDescription(event['description'], event['keywords'], event['title']);
            });

    }

метод, который обновляет заголовок и метатеги → вызывается из ngOnInit():

updateDescription(desc: string, keywords: string, title: string) {
    this.titleService.setTitle(title);
    this.meta.updateTag({ name: 'description', content: desc })
    this.meta.updateTag({ name: 'keywords', content: keywords })
    this.meta.updateTag({ name: 'og:title', content: title })
    this.meta.updateTag({ name: 'og:description', content: desc })
}

Надеюсь, поможет.