Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | 2x 2x 2x 2x 14x 14x 14x 4x 4x 4x 4x 4x 4x 4x 4x 4x 2x 4x 4x 4x 4x 2x 4x 2x 4x 4x 7x 7x 5x 5x 4x 4x 4x 5x 4x 4x 4x 3x 3x 2x 2x 2x 2x 3x | import { Inject, Injectable } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Meta, Title } from '@angular/platform-browser';
export interface SeoMetadata {
title: string;
description: string;
keywords: string[];
canonicalUrl?: string;
imageUrl?: string;
twitterHandle?: string;
}
@Injectable({ providedIn: 'root' })
export class SeoService {
constructor(
private readonly meta: Meta,
private readonly title: Title,
@Inject(DOCUMENT) private readonly document: Document,
) {}
setDefaultTags(metaData: SeoMetadata): void {
this.title.setTitle(metaData.title);
this.meta.updateTag({ name: 'description', content: metaData.description });
this.meta.updateTag({ name: 'keywords', content: metaData.keywords.join(', ') });
this.meta.updateTag({ name: 'robots', content: 'index, follow' });
this.meta.updateTag({ name: 'theme-color', content: '#a33360' });
// Open Graph
this.meta.updateTag({ property: 'og:title', content: metaData.title });
this.meta.updateTag({ property: 'og:description', content: metaData.description });
this.meta.updateTag({ property: 'og:type', content: 'website' });
if (metaData.imageUrl) {
this.meta.updateTag({ property: 'og:image', content: metaData.imageUrl });
}
// Twitter
this.meta.updateTag({ name: 'twitter:card', content: 'summary_large_image' });
this.meta.updateTag({ name: 'twitter:title', content: metaData.title });
this.meta.updateTag({ name: 'twitter:description', content: metaData.description });
if (metaData.imageUrl) {
this.meta.updateTag({ name: 'twitter:image', content: metaData.imageUrl });
}
if (metaData.twitterHandle) {
this.meta.updateTag({ name: 'twitter:creator', content: metaData.twitterHandle });
}
const canonicalUrl = metaData.canonicalUrl ?? this.document?.location?.href ?? '';
this.setCanonical(canonicalUrl);
}
setCanonical(url: string): void {
const head = this.document.head;
if (!head) return;
let linkEl = head.querySelector("link[rel='canonical']") as HTMLLinkElement | null;
if (!linkEl) {
linkEl = this.document.createElement('link');
linkEl.setAttribute('rel', 'canonical');
head.appendChild(linkEl);
}
linkEl.setAttribute('href', url);
}
attachStructuredData(schema: Record<string, unknown>): void {
const scriptId = 'pnb-structured-data';
const head = this.document.head;
if (!head) return;
let scriptEl = this.document.getElementById(scriptId) as HTMLScriptElement | null;
if (!scriptEl) {
scriptEl = this.document.createElement('script');
scriptEl.id = scriptId;
scriptEl.type = 'application/ld+json';
head.appendChild(scriptEl);
}
scriptEl.textContent = JSON.stringify(schema, null, 2);
}
}
|