在Angular中基于路由动态添加元描述

时间:2018-01-18 21:23:45

标签: angular typescript angular-ui-router

我正在使用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那种人。

6 个答案:

答案 0 :(得分:30)

首先创建一个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特定标签。

答案 1 :(得分:6)

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

答案 2 :(得分:4)

Angular 6+和RxJS 6+解决方案,可在路线更改时动态设置标题

如果/当您升级到Angular 6时,这里就是解决方案。

此服务将:

  • 在路线更改时更新元标题。
  • 出于任何原因替换标题的选项。

将SEO /元服务创建/更改为以下内容。

(d)

导入您的服务并在构造器中调用它。

app.component.ts

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

这仍然需要格式化这样的路由。

路由文件.ts

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

希望这对您和其他希望在Angular 6中动态更新标题/元数据的人有所帮助。

答案 3 :(得分:1)

以下是我项目中的相关部分。 (角度2/4)


app-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 })
}

希望有帮助。

答案 4 :(得分:1)

由于Angular页面是在客户端渲染的,因此搜寻器无法检测到元标记。由于未检测到动态元标记,大多数爬网程序在运行时不执行javascript。即使对于Facebook和Twitter也是如此。

需要使用 Angular Universal进行服务器端渲染或预渲染服务,例如 prerender.io

答案 5 :(得分:1)

IMO 在 Routes 数组中使用数据(标题、描述等)不是那么一致,我们可以将所有数据聚合到一个地方。同样在 2021 年,我们可以开箱即用地使用常见的 Angular 工具:

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