在 Angular 中根据路由动态添加元描述

Posted

技术标签:

【中文标题】在 Angular 中根据路由动态添加元描述【英文标题】:Dynamically add meta description based on route in Angular 【发布时间】:2018-06-28 01:42:16 【问题描述】:

我正在使用 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']);
      );
  

所以我的问题是,有没有办法使用相同的方法动态设置元描述?如果有办法结合页面标题和元描述功能,那将是理想的。

我的 Angular 培训非常有限,所以这可能是个小问题。我更像是一个设计师/css/html 类型的人。

【问题讨论】:

从 Angular 4 开始,已经有一个内置服务可以从 '@angular/platform-b​​rowser' 导入 Meta ;看这里angular.io/api/platform-browser/Meta @elasticrash 是的,我研究了 Meta。我更感兴趣的是如何将 Meta 应用到我已经拥有的东西中。 元描述在哪里? @Melchia 如果可能的话,我想存储我的元描述,页面标题是 app.module.ts。 你不能只在数据中添加一个属性描述并做你在 ngOnInit 中做的同样的事情吗? 【参考方案1】:

首先创建一个 SEOService 或类似下面的东西:

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 )
  

在你的组件(最好是app.component.ts)中注入SEOService后,在OnInit方法中设置元标签和标题

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 特定标签。

【讨论】:

我已经准备好了一切,但“_seoService”在 app.component 中给我带来了问题。它说'[ts] Property 'seoService' 在类型'AppComponent' 上不存在。”这是有道理的。我应该如何定义“_seoService”? 在您的 app.module.ts 提供者部分导入 SEOService。然后在您设置元标记和标题的组件中,在构造函数中注入 SEOService,例如 constructor(public _seoService: SEOService) 万岁奥坎!似乎像魅力一样工作。谢谢! 同时导入以下内容:import 'rxjs/add/operator/filter'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/mergeMap'; 在您的数据部分,您如何处理“:”?例如,您有 title: 和 description: 不带引号。你如何处理数据部分中的 og:url?【参考方案2】:

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

【讨论】:

我会试试这个,如果可行,那么这就是答案 我一直在寻找一种快速、简单、正确的方法来更新标题标签和元数据,就是这样!非常感谢您提供这个。如果我可以将其标记为答案,我完全愿意。但是,如果我可以稍作修正,那些“this.meta ...”项需要放在构造函数括号 内。这就是它对我有用的方式。构造函数(公共标题:标题,公共元:元) this.title.setTitle('这是我的页面标题'); this.meta.updateTag( name: 'description', content: '这是我的描述'); @michaelt 很高兴它对你有用。 ... 表示 this.meta 在同一个类中的某处使用。它可以是ngOnInit 方法或其他。 @EstusFlask 这会被搜索引擎抓取吗? @pravindot17 是的,如果您使用 Angular s-s-r。如果你不使用它,你的问题基本上是动态设置document.title等是否对搜索引擎友好,答案是它或多或少只对谷歌友好。【参考方案3】:

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

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

路由文件.ts

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

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

【讨论】:

你在哪里调用 updateMetaInfo() 函数?应该在 app.component.ts 作为 this.meta.updateTitle();?【参考方案4】:

以下是我项目中的相关部分。 (角度 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 )

希望对您有所帮助。

【讨论】:

【参考方案5】:

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

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

编辑:- 我为此找到了一个很好的 youtube 教程。下面是链接, https://www.youtube.com/watch?v=lncsmB5yfzE

【讨论】:

Google 现在正在渲染页面,而大多数其他搜索引擎也在这样做。 Facebook 仍然没有。如果您在 Facebook 开发人员工具中进行测试,您会发现它仍然无法获取标签。【参考方案6】:

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

【讨论】:

这是一个优雅的解决方案

以上是关于在 Angular 中根据路由动态添加元描述的主要内容,如果未能解决你的问题,请参考以下文章

Angular 5 - 添加了动态路由但没有路由

如何在 Angular 中使用 Ag Grid 根据列值组为行添加颜色?

Angular 4 - 为 Facebook 动态更新 Meta 标签(打开图表)

根据从 Angular 组件返回的项目宽度动态设置容器的宽度

Angular 中动态编译的延迟加载动态路由导致“不安全评估”错误

如何使用 Angular 4 元服务动态更新打开的图形标签? [复制]