From 979c0b562b023236803d33f672ad34e9bc01520a Mon Sep 17 00:00:00 2001 From: Vladimir Lugovsky Date: Fri, 20 Dec 2019 11:09:37 +0300 Subject: [PATCH] feat(seo): add canonical tag (#5578) --- src/app/@core/core.module.ts | 67 +++++++++++++++++++++++++++++- src/app/@core/utils/index.ts | 14 ++++++- src/app/@core/utils/seo.service.ts | 58 ++++++++++++++++++++++++++ src/app/app.component.ts | 4 +- 4 files changed, 140 insertions(+), 3 deletions(-) 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 68cb4ba9..012f8097 100644 --- a/src/app/@core/core.module.ts +++ b/src/app/@core/core.module.ts @@ -5,9 +5,52 @@ import { NbSecurityModule, NbRoleProvider } from '@nebular/security'; import { of as observableOf } from 'rxjs'; import { throwIfAlreadyLoaded } from './module-import-guard'; -import { AnalyticsService } from './utils'; +import { + AnalyticsService, + LayoutService, + PlayerService, + SeoService, + StateService, +} from './utils'; import { UserData } from './data/users'; +import { ElectricityData } from './data/electricity'; +import { SmartTableData } from './data/smart-table'; +import { UserActivityData } from './data/user-activity'; +import { OrdersChartData } from './data/orders-chart'; +import { ProfitChartData } from './data/profit-chart'; +import { TrafficListData } from './data/traffic-list'; +import { EarningData } from './data/earning'; +import { OrdersProfitChartData } from './data/orders-profit-chart'; +import { TrafficBarData } from './data/traffic-bar'; +import { ProfitBarAnimationChartData } from './data/profit-bar-animation-chart'; +import { TemperatureHumidityData } from './data/temperature-humidity'; +import { SolarData } from './data/solar'; +import { TrafficChartData } from './data/traffic-chart'; +import { StatsBarData } from './data/stats-bar'; +import { CountryOrderData } from './data/country-order'; +import { StatsProgressBarData } from './data/stats-progress-bar'; +import { VisitorsAnalyticsData } from './data/visitors-analytics'; +import { SecurityCamerasData } from './data/security-cameras'; + import { UserService } from './mock/users.service'; +import { ElectricityService } from './mock/electricity.service'; +import { SmartTableService } from './mock/smart-table.service'; +import { UserActivityService } from './mock/user-activity.service'; +import { OrdersChartService } from './mock/orders-chart.service'; +import { ProfitChartService } from './mock/profit-chart.service'; +import { TrafficListService } from './mock/traffic-list.service'; +import { EarningService } from './mock/earning.service'; +import { OrdersProfitChartService } from './mock/orders-profit-chart.service'; +import { TrafficBarService } from './mock/traffic-bar.service'; +import { ProfitBarAnimationChartService } from './mock/profit-bar-animation-chart.service'; +import { TemperatureHumidityService } from './mock/temperature-humidity.service'; +import { SolarService } from './mock/solar.service'; +import { TrafficChartService } from './mock/traffic-chart.service'; +import { StatsBarService } from './mock/stats-bar.service'; +import { CountryOrderService } from './mock/country-order.service'; +import { StatsProgressBarService } from './mock/stats-progress-bar.service'; +import { VisitorsAnalyticsService } from './mock/visitors-analytics.service'; +import { SecurityCamerasService } from './mock/security-cameras.service'; import { MockDataModule } from './mock/mock-data.module'; const socialLinks = [ @@ -30,6 +73,24 @@ const socialLinks = [ const DATA_SERVICES = [ { provide: UserData, useClass: UserService }, + { provide: ElectricityData, useClass: ElectricityService }, + { provide: SmartTableData, useClass: SmartTableService }, + { provide: UserActivityData, useClass: UserActivityService }, + { provide: OrdersChartData, useClass: OrdersChartService }, + { provide: ProfitChartData, useClass: ProfitChartService }, + { provide: TrafficListData, useClass: TrafficListService }, + { provide: EarningData, useClass: EarningService }, + { provide: OrdersProfitChartData, useClass: OrdersProfitChartService }, + { provide: TrafficBarData, useClass: TrafficBarService }, + { provide: ProfitBarAnimationChartData, useClass: ProfitBarAnimationChartService }, + { provide: TemperatureHumidityData, useClass: TemperatureHumidityService }, + { provide: SolarData, useClass: SolarService }, + { provide: TrafficChartData, useClass: TrafficChartService }, + { provide: StatsBarData, useClass: StatsBarService }, + { provide: CountryOrderData, useClass: CountryOrderService }, + { provide: StatsProgressBarData, useClass: StatsProgressBarService }, + { provide: VisitorsAnalyticsData, useClass: VisitorsAnalyticsService }, + { provide: SecurityCamerasData, useClass: SecurityCamerasService }, ]; export class NbSimpleRoleProvider extends NbRoleProvider { @@ -78,6 +139,10 @@ export const NB_CORE_PROVIDERS = [ provide: NbRoleProvider, useClass: NbSimpleRoleProvider, }, AnalyticsService, + LayoutService, + PlayerService, + SeoService, + StateService, ]; @NgModule({ diff --git a/src/app/@core/utils/index.ts b/src/app/@core/utils/index.ts index 58bd2917..1b3f94da 100644 --- a/src/app/@core/utils/index.ts +++ b/src/app/@core/utils/index.ts @@ -1 +1,13 @@ -export { AnalyticsService } from './analytics.service'; +import { LayoutService } from './layout.service'; +import { AnalyticsService } from './analytics.service'; +import { PlayerService } from './player.service'; +import { StateService } from './state.service'; +import { SeoService } from './seo.service'; + +export { + LayoutService, + AnalyticsService, + PlayerService, + SeoService, + StateService, +}; diff --git a/src/app/@core/utils/seo.service.ts b/src/app/@core/utils/seo.service.ts new file mode 100644 index 00000000..50f877d7 --- /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 f46b6525..faf0c5c4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,6 +5,7 @@ */ import { Component, OnInit } from '@angular/core'; import { AnalyticsService } from './@core/utils/analytics.service'; +import { SeoService } from './@core/utils/seo.service'; @Component({ selector: 'ngx-app', @@ -12,10 +13,11 @@ import { AnalyticsService } from './@core/utils/analytics.service'; }) export class AppComponent implements OnInit { - constructor(private analytics: AnalyticsService) { + constructor(private analytics: AnalyticsService, private seoService: SeoService) { } ngOnInit() { this.analytics.trackPageViews(); + this.seoService.trackCanonicalChanges(); } }