【问题标题】:how to add canonical link in angular 5如何在 Angular 5 中添加规范链接
【发布时间】:2018-06-07 09:34:40
【问题描述】:

如何在 Angular 5 中动态添加规范链接

<link rel="canonical" href="http://foobar.com/gotcah"/>

【问题讨论】:

  • 我认为 href 是对这个问题的接收的泄漏

标签: angular angular5 meta-tags canonical-link


【解决方案1】:

面对同样的问题,我四处搜索并找到了如何执行此操作的指南:

https://www.concretepage.com/angular/angular-title-service-and-canonical-url

虽然它使用的是 Angular 6,但我认为它向后兼容 5。

它基本上建议创建一个服务 (SEOService) 以方便在注入的任何位置创建规范链接。它将 Angular DOCUMENT 对象注入到服务中,并将规范链接元素创建为 HTMLLinkElement

注意该解决方案是预渲染/服务器端渲染友好的 - 因此,如果您希望更好地控制应用程序中页面的 SEO,我相信这就是您想要的。

这是对文章中服务的最小重写:

seo.service.ts

import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class SeoService { 
   
  constructor(@Inject(DOCUMENT) private doc) {}
  
  createLinkForCanonicalURL() {
     let link: HTMLLinkElement = this.doc.createElement('link');
     link.setAttribute('rel', 'canonical');
     this.doc.head.appendChild(link);
     link.setAttribute('href', this.doc.URL);
   }
} 

下面是使用服务的组件的重写:

data.component.ts

import { Component, OnInit } from '@angular/core';
import { SeoService } from './seo.service';

@Component({
  selector: 'app-data',
  templateUrl: './data.component.html'
})
export class DataComponent implements OnInit {
  
  constructor(private seoService: SeoService) { }
  
  ngOnInit() {
    this.createLinkForCanonicalURL();
  }
  
  createLinkForCanonicalURL() {
    this.seoService.createLinkForCanonicalURL();
  } 
}

您可以简单地让 createLinkForCanonicalURL() 方法采用您希望作为页面规范引用的 URL 的可选参数,以实现完全控制。

【讨论】:

  • 这应该是已接受的答案
  • 当用户导航到另一个页面时,canonical Tag没有被移除是否有问题?
  • @ThomasZoé 可能不会,因为这只是一个客户端问题。
  • 但我相信应该先从 DOM 中删除之前的规范标签,然后再在导航中添加另一个标签,不是吗?
  • 我正面临规范的问题是作为 Http 链接来的,但我的网站在 Https 中。如何解决此问题。
【解决方案2】:

这适用于 Angular 8 或 9,并将向您网站的每个页面添加规范 URL。

  1. 创建规范服务:
// shared/canonical/canonical.service.ts


import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class CanonicalService {
  constructor(@Inject(DOCUMENT) private dom) {}

  setCanonicalURL(url?: string) {
    const canURL = url == undefined ? this.dom.URL : url;
    const link: HTMLLinkElement = this.dom.createElement('link');
    link.setAttribute('rel', 'canonical');
    this.dom.head.appendChild(link);
    link.setAttribute('href', canURL);
  }
}
  1. 引用规范服务并在应用组件中调用它
// app.component.ts
import { CanonicalService } from './shared/services/canonical/canonical.service';
...

ngOnInit() {
  this.canonicalService.setCanonicalURL();
}
...

【讨论】:

    【解决方案3】:

    我得到了解决方案, 创建链接服务(例如:link.service.ts)并粘贴以下代码:

    import { Injectable, Optional, RendererFactory2, ViewEncapsulation, Inject } from '@angular/core';
    import { DOCUMENT } from '@angular/platform-browser';
    
    @Injectable()
    export class LinkService {
    
    constructor(
        private rendererFactory: RendererFactory2,
        @Inject(DOCUMENT) private document
    ) {
    }
    
    /**
     * Inject the State into the bottom of the <head>
     */
    addTag(tag: LinkDefinition, forceCreation?: boolean) {
    
        try {
            const renderer = this.rendererFactory.createRenderer(this.document, {
                id: '-1',
                encapsulation: ViewEncapsulation.None,
                styles: [],
                data: {}
            });
    
            const link = renderer.createElement('link');
            const head = this.document.head;
            const selector = this._parseSelector(tag);
    
            if (head === null) {
                throw new Error('<head> not found within DOCUMENT.');
            }
    
            Object.keys(tag).forEach((prop: string) => {
                return renderer.setAttribute(link, prop, tag[prop]);
            });
    
            // [TODO]: get them to update the existing one (if it exists) ?
            renderer.appendChild(head, link);
    
        } catch (e) {
            console.error('Error within linkService : ', e);
        }
    }
    
    private _parseSelector(tag: LinkDefinition): string {
        // Possibly re-work this
        const attr: string = tag.rel ? 'rel' : 'hreflang';
        return `${attr}="${tag[attr]}"`;
    }
    }
    
     export declare type LinkDefinition = {
      charset?: string;
      crossorigin?: string;
      href?: string;
      hreflang?: string;
      media?: string;
      rel?: string;
      rev?: string;
      sizes?: string;
      target?: string;
      type?: string;
    } & {
        [prop: string]: string;
    };
    

    在组件中导入服务:

    import { LinkService } from '../link.service';
    
    constructor(private linkService: LinkService) {
    
    this.linkService.addTag( { rel: 'canonical', href: 'url here'} );
    }
    

    请参考以下链接:

    https://github.com/angular/angular/issues/15776

    【讨论】:

    • 难道类中的parseSelector方法根本不需要?
    • 代码中没有使用 3 个变量和 1 个方法
    【解决方案4】:

    如果您想动态添加规范链接,请创建一个简单的 moveToHead 指令,如下所示:

    @Directive({
      selector: '[appMoveToHead]'
    })
    export class MoveToHeadDirective implements OnDestroy, OnInit {
    
      constructor(private renderer: Renderer2, 
                  private elRef: ElementRef, 
                  @Inject(DOCUMENT) private document: Document) {
    
      }
    
    ngOnInit(): void {
        this.renderer.appendChild(this.document.head, this.elRef.nativeElement);
        this.renderer.removeAttribute(this.elRef.nativeElement, 'appmovetohead');
      }
    
      ngOnDestroy(): void {
        this.renderer.removeChild(this.document.head, this.elRef.nativeElement);
      }
    }
    
    

    在这里,当用户导航到另一个页面时,我们将从 DOM 中删除规范标签。我们在 MoveToHead 指令的ngOnDestroy() 中执行此操作。这样就处理了新页面中存在之前的规范标签的情况。

    用法: 在任何component.ts中

    canonicalLink:string;
    constructor(private sanitizer: DomSanitizer) { }
    //In oninit or when your data is ready, generate canonical link
    ngOnInit() {
    
        this.blogsService.getBlog(this.blogId).subscribe(data => {
           let canLink = "https://example.com/blog/"+data.blogId;
           // You can use pipe for sanitizing but lets do it here
           this.canonicalLink = this.sanitizer.bypassSecurityTrustResourceUrl(canLink);
        });
    }
    

    在任何 component.html 模板中:

    <div *ngIf="canonicalLink">
      <link  rel="canonical" appMoveToHead [attr.href]="canonicalLink" />
    </div>
    
    <div>
      <!-- Other template html -->
    </div>
    

    通过这种方式,您可以使用数据绑定和所有其他东西,并像对待模板中的任何其他元素一样对待metalink 等。 例如-

    <link rel="amphtml" moveToHead [attr.href]="ampUrl">
    

    此方法也适用于 Angular Universal (SSR)。

    请参考以下链接-

    https://github.com/angular/angular/issues/15776

    【讨论】:

      【解决方案5】:

      要销毁现有链接,请在创建新链接之前调用此链接:

      destroyLinkForCanonicalURL() {
          const els = this.document.querySelectorAll('link[rel=\'canonical\']');
          for (let i = 0, l = els.length; i < l; i++) {
            const el = els[i];
            el.remove();
          }
        }
      

      this 解决方案完美搭配。

      它还可以防止在 SSR 模式下重复链接。

      【讨论】:

        猜你喜欢
        • 2017-02-07
        • 1970-01-01
        • 1970-01-01
        • 2019-12-22
        • 1970-01-01
        • 2018-07-24
        • 2023-04-06
        • 2020-05-13
        • 1970-01-01
        相关资源
        最近更新 更多