From d25cc1a4fbd18443ba7d12bbed512cf213fe34ac Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Fri, 20 Dec 2019 11:14:42 +0300 Subject: [PATCH] feat(seo): add canonical tag (#5580) --- src/app/@core/core.module.ts | 2 ++ src/app/@core/utils/index.ts | 1 + src/app/@core/utils/seo.service.ts | 58 ++++++++++++++++++++++++++++++ src/app/app.component.ts | 5 ++- 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/app/@core/utils/seo.service.ts diff --git a/src/app/@core/core.module.ts b/src/app/@core/core.module.ts index bd141dfe8f..b8e96c7ad4 100644 --- a/src/app/@core/core.module.ts +++ b/src/app/@core/core.module.ts @@ -9,6 +9,7 @@ import { AnalyticsService, LayoutService, PlayerService, + SeoService, StateService, } from './utils'; import { UserData } from './data/users'; @@ -141,6 +142,7 @@ export const NB_CORE_PROVIDERS = [ AnalyticsService, LayoutService, PlayerService, + SeoService, StateService, AbService, ]; diff --git a/src/app/@core/utils/index.ts b/src/app/@core/utils/index.ts index af89022193..a3bf2b903d 100644 --- a/src/app/@core/utils/index.ts +++ b/src/app/@core/utils/index.ts @@ -2,3 +2,4 @@ export { LayoutService } from './layout.service'; export { AnalyticsService } from './analytics.service'; export { PlayerService } from './player.service'; export { StateService } from './state.service'; +export { SeoService } from './seo.service'; diff --git a/src/app/@core/utils/seo.service.ts b/src/app/@core/utils/seo.service.ts new file mode 100644 index 0000000000..50f877d754 --- /dev/null +++ b/src/app/@core/utils/seo.service.ts @@ -0,0 +1,58 @@ +import { Injectable, Inject, PLATFORM_ID, OnDestroy } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; +import { NavigationEnd, Router } from '@angular/router'; +import { NB_DOCUMENT } from '@nebular/theme'; +import { filter, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; + +@Injectable() +export class SeoService implements OnDestroy { + + private readonly destroy$ = new Subject(); + private readonly dom: Document; + private readonly isBrowser: boolean; + private linkCanonical: HTMLLinkElement; + + constructor( + private router: Router, + @Inject(NB_DOCUMENT) document, + @Inject(PLATFORM_ID) platformId, + ) { + this.isBrowser = isPlatformBrowser(platformId); + this.dom = document; + + if (this.isBrowser) { + this.createCanonicalTag(); + } + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + createCanonicalTag() { + this.linkCanonical = this.dom.createElement('link'); + this.linkCanonical.setAttribute('rel', 'canonical'); + this.dom.head.appendChild(this.linkCanonical); + this.linkCanonical.setAttribute('href', this.getCanonicalUrl()); + } + + trackCanonicalChanges() { + if (!this.isBrowser) { + return; + } + + this.router.events.pipe( + filter((event) => event instanceof NavigationEnd), + takeUntil(this.destroy$), + ) + .subscribe(() => { + this.linkCanonical.setAttribute('href', this.getCanonicalUrl()); + }); + } + + private getCanonicalUrl(): string { + return this.dom.location.origin + this.dom.location.pathname; + } +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d240cb6c79..a80f9064dd 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -10,6 +10,7 @@ import { NbThemeService } from '@nebular/theme'; import { AnalyticsService } from './@core/utils/analytics.service'; import { AbService } from './@core/utils/ab.service'; import { withLatestFrom, filter } from 'rxjs/operators'; +import { SeoService } from './@core/utils/seo.service'; @Component({ selector: 'ngx-app', @@ -22,7 +23,8 @@ export class AppComponent implements OnInit { constructor(private analytics: AnalyticsService, private activatedRoute: ActivatedRoute, private abService: AbService, - private themeService: NbThemeService) { + private themeService: NbThemeService, + private seoService: SeoService) { this.themeService.onThemeChange() .subscribe((theme: any) => { @@ -58,5 +60,6 @@ export class AppComponent implements OnInit { this.themeService.changeTheme(themeName); } }); + this.seoService.trackCanonicalChanges(); } }