Skip to content

Commit

Permalink
Create the services for more comfortable way of handle SEO data #5
Browse files Browse the repository at this point in the history
- create TransferStateService to transfer the  payload from the server to client
- create SeoPropertiesService to handle the SEO data in one place
- modify HttpResponseStatusService, DocumentTitleService, DocumentMetaService, DocumentLinkService to work with TransferStateService
- temporary fix the issue with ngOnDestroy() hook in the components. It calls every time on the server side when the browser page refreshes.
  • Loading branch information
athlonUA committed Aug 15, 2018
1 parent c8e5ee0 commit 1a547d0
Show file tree
Hide file tree
Showing 16 changed files with 457 additions and 171 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { TestBed, inject } from '@angular/core/testing';
import { TransferState } from '@angular/platform-browser';

import { DocumentLinkService } from './document-link.service';
import { TransferStateService } from '../transfer-state/transfer-state.service';

describe('DocumentLinkService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [DocumentLinkService],
});
TestBed.configureTestingModule({ providers: [DocumentLinkService, TransferState, TransferStateService] });
});

it(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

import { CoreModule } from '../../core.module';
import { TransferStateService } from '../transfer-state/transfer-state.service';

export declare type LinkDefinition = {
charset?: string;
Expand All @@ -18,32 +19,75 @@ export declare type LinkDefinition = {
[prop: string]: string;
};

interface PayloadDefinition {
payload: {
addTagsExecuted?: boolean;
removeTagExecuted?: boolean;
};
}

@Injectable({
providedIn: CoreModule,
})
export class DocumentLinkService {
constructor(@Inject(DOCUMENT) private document) {}
private payloadServerName = 'DocumentLinkServicePayload';

constructor(@Inject(DOCUMENT) private document, private transferStateService: TransferStateService) {}

public addTags(tags: LinkDefinition[]): void {
this.transferStateService
.savePayload(
() => {
tags.map((tag: LinkDefinition) => this.addTag(tag));

return new Promise(resolve => resolve({ payload: { addTagsExecuted: true } }));
},
this.payloadServerName,
{ payload: null }
)
.then(
(payload: PayloadDefinition) => {
// payload received
},
error => {
// error while receiving payload
}
);
}

public removeTags(attrSelectors: string[]): void {
this.transferStateService
.savePayload(
() => {
attrSelectors.map((attrSelector: string) => this.removeTag(attrSelector));

public addTag(tag: LinkDefinition): void {
return new Promise(resolve => resolve({ payload: { removeTagsExecuted: true } }));
},
this.payloadServerName,
{ payload: null }
)
.then(
(payload: PayloadDefinition) => {
// payload received
},
error => {
// error while receiving payload
}
);
}

private addTag(tag: LinkDefinition): void {
const link: HTMLLinkElement = this.document.createElement('link');
Object.keys(tag).map((prop: string) => {
link.setAttribute(prop, tag[prop]);
});
this.document.head.appendChild(link);
}

public addTags(tags: LinkDefinition[]): void {
tags.map((tag: LinkDefinition) => this.addTag(tag));
}

public removeTag(attrSelector: string): void {
private removeTag(attrSelector: string): void {
const linkTags: HTMLLinkElement[] = this.document.querySelectorAll('link[' + attrSelector + ']');
for (const link of linkTags) {
this.document.head.removeChild(link);
}
}

public removeTags(attrSelectors: string[]): void {
attrSelectors.map((attrSelector: string) => this.removeTag(attrSelector));
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { TestBed, inject } from '@angular/core/testing';
import { TransferState } from '@angular/platform-browser';

import { DocumentMetaService } from './document-meta.service';
import { TransferStateService } from '../transfer-state/transfer-state.service';

describe('DocumentMetaService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [DocumentMetaService],
});
TestBed.configureTestingModule({ providers: [DocumentMetaService, TransferState, TransferStateService] });
});

it(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,67 @@ import { Injectable } from '@angular/core';
import { Meta, MetaDefinition } from '@angular/platform-browser';

import { CoreModule } from '../../core.module';
import { TransferStateService } from '../transfer-state/transfer-state.service';

interface PayloadDefinition {
payload: {
addTagsExecuted?: boolean;
updateTagExecuted?: boolean;
removeTagExecuted?: boolean;
};
}

@Injectable({
providedIn: CoreModule,
})
export class DocumentMetaService {
constructor(private metaService: Meta) {}
private payloadServerName = 'DocumentMetaServicePayload';

public addTag(tag: MetaDefinition, forceCreation: boolean = false): HTMLMetaElement | null {
return this.metaService.addTag(tag, forceCreation);
}
constructor(private metaService: Meta, private transferStateService: TransferStateService) {}

public addTags(tags: MetaDefinition[], forceCreation: boolean = false): HTMLMetaElement[] {
return this.metaService.addTags(tags, forceCreation);
}
public addTags(tags: MetaDefinition[], forceCreation: boolean = false): void {
this.transferStateService
.savePayload(
() => {
this.metaService.addTags(tags, forceCreation);

public getTag(attrSelector: string): HTMLMetaElement | null {
return this.metaService.getTag(attrSelector);
return new Promise(resolve => resolve({ payload: { addTagsExecuted: true } }));
},
this.payloadServerName,
{ payload: null }
)
.then(
(payload: PayloadDefinition) => {
// payload received
},
error => {
// error while receiving payload
}
);
}

public getTags(attrSelector: string): HTMLMetaElement[] {
return this.metaService.getTags(attrSelector);
}
public removeTags(attrSelectors: string[]): void {
this.transferStateService
.savePayload(
() => {
attrSelectors.map((attrSelector: string) => this.removeTag(attrSelector));

public updateTag(tag: MetaDefinition, selector?: string): HTMLMetaElement | null {
return this.metaService.updateTag(tag, selector);
return new Promise(resolve => resolve({ payload: { removeTagsExecuted: true } }));
},
this.payloadServerName,
{ payload: null }
)
.then(
(payload: PayloadDefinition) => {
// payload received
},
error => {
// error while receiving payload
}
);
}

public removeTag(attrSelector: string): void {
private removeTag(attrSelector: string): void {
this.metaService.removeTag(attrSelector);
}

public removeTags(attrSelectors: string[]): void {
attrSelectors.map((attrSelector: string) => this.removeTag(attrSelector));
}

public removeTagElement(meta: HTMLMetaElement): void {
this.metaService.removeTagElement(meta);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { TestBed, inject } from '@angular/core/testing';
import { TransferState } from '@angular/platform-browser';

import { DocumentTitleService } from './document-title.service';
import { TransferStateService } from '../transfer-state/transfer-state.service';

describe('DocumentTitleService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [DocumentTitleService],
});
TestBed.configureTestingModule({ providers: [DocumentTitleService, TransferState, TransferStateService] });
});

it(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,44 @@ import { Injectable } from '@angular/core';
import { Title } from '@angular/platform-browser';

import { CoreModule } from '../../core.module';
import { TransferStateService } from '../transfer-state/transfer-state.service';

interface PayloadDefinition {
payload: {
setTitleExecuted: boolean;
};
}

@Injectable({
providedIn: CoreModule,
})
export class DocumentTitleService {
constructor(private titleService: Title) {}
private payloadServerName = 'DocumentTitleServicePayload';

constructor(private titleService: Title, private transferStateService: TransferStateService) {}

public setTitle(value: string): void {
this.titleService.setTitle(value);
}
this.transferStateService
.savePayload(
() => {
this.titleService.setTitle(value);

public getTitle(): string {
return this.titleService.getTitle();
return new Promise(resolve => resolve({ payload: { setTitleExecuted: true } }));
},
this.payloadServerName,
{ payload: null }
)
.then(
(payload: PayloadDefinition) => {
// payload received
},
error => {
// error while receiving payload
}
);
}

public removeTitle(): void {
this.titleService.setTitle('');
this.setTitle('');
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { TestBed, inject } from '@angular/core/testing';
import { TransferState } from '@angular/platform-browser';

import { HttpResponseStatusService } from './http-response-status.service';
import { TransferStateService } from '../transfer-state/transfer-state.service';

describe('HttpResponseStatusService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [HttpResponseStatusService],
providers: [HttpResponseStatusService, TransferState, TransferStateService],
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,48 @@ import { Inject, Injectable, Optional } from '@angular/core';
import { RESPONSE } from '@nguniversal/express-engine/tokens';

import { CoreModule } from '../../core.module';
import { TransferStateService } from '../transfer-state/transfer-state.service';

interface PayloadDefinition {
payload: {
setStatusExecuted: boolean;
};
}

@Injectable({
providedIn: CoreModule,
})
export class HttpResponseStatusService {
private payloadServerName = 'HttpResponseStatusServicePayload';

constructor(
@Optional()
@Inject(RESPONSE)
private res: any
private res: any,
private transferStateService: TransferStateService
) {}

public setStatus(code: number, message: string): void {
if (this.res) {
this.res.statusCode = code;
this.res.statusMessage = message;
}
this.transferStateService
.savePayload(
() => {
if (this.res) {
this.res.statusCode = code;
this.res.statusMessage = message;
}

return new Promise(resolve => resolve({ payload: { setStatusExecuted: true } }));
},
this.payloadServerName,
{ payload: null }
)
.then(
(payload: PayloadDefinition) => {
// payload received
},
error => {
// error while receiving payload
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { TestBed, inject } from '@angular/core/testing';
import { TransferState } from '@angular/platform-browser';

import { SeoPropertiesService } from './seo-properties.service';
import { DocumentTitleService } from '../document-title/document-title.service';
import { TransferStateService } from '../transfer-state/transfer-state.service';
import { DocumentMetaService } from '../document-meta/document-meta.service';
import { DocumentLinkService } from '../document-link/document-link.service';

describe('SeoPropertiesService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
SeoPropertiesService,
DocumentTitleService,
TransferState,
TransferStateService,
DocumentMetaService,
DocumentLinkService,
],
});
});

it(
'should be created',
inject([SeoPropertiesService], (service: SeoPropertiesService) => {
expect(service).toBeTruthy();
})
);
});
Loading

0 comments on commit 1a547d0

Please sign in to comment.