seo.service.ts
· 4.5 KiB · TypeScript
Raw
import { Injectable, inject } from "@angular/core";
import { Title, Meta } from "@angular/platform-browser";
import { DOCUMENT } from "@angular/common";
import { RendererFactory2 } from "@angular/core";
import { SeoData } from "@core/models/seo.model";
@Injectable({ providedIn: "root" })
export class SeoService {
private titleService = inject(Title);
private metaService = inject(Meta);
private document = inject(DOCUMENT);
private rendererFactory = inject(RendererFactory2);
private renderer =
this.rendererFactory.createRenderer(null, null);
// Constants for metadata
private readonly BRAND = "Hurler Webdesign";
private readonly FALLBACK_IMAGE =
'https://hurler-webdesign.de/assets/og-default.jpg';
private readonly DEFAULT_TYPE = "website";
/**
* Updates SEO metadata with the provided data.
*
* @param data - The SeoData object containing title, description, type, and image.
* @param canonicalPath - Optional parameter for the canonical URL path.
*/
updateMetadata(data: SeoData, canonicalPath?: string) {
const fullTitle = `${data.title} | ${this.BRAND}`;
const url = `https://hurler-webdesign.de${canonicalPath || ""}`;
// Update title and meta description
this.titleService.setTitle(fullTitle);
this.metaService.updateTag({ name: "description", content: data.description });
// Open Graph metadata
this.metaService.updateTag({
property: "og:title",
content: fullTitle,
});
this.metaService.updateTag({
property: "og:description",
content: data.description,
});
this.metaService.updateTag({
property: "og:type",
content: data.type || this.DEFAULT_TYPE,
});
this.metaService.updateTag({
property: "og:image",
content: data.image || this.FALLBACK_IMAGE,
});
this.metaService.updateTag({ property: "og:url", content: url });
// Twitter card metadata
this.metaService.updateTag({
name: "twitter:card",
content: "summary_large_image",
});
this.metaService.updateTag({
name: "twitter:title",
content: fullTitle,
});
this.metaService.updateTag({
name: "twitter:description",
content: data.socialsDescription || data.description,
});
this.metaService.updateTag({ name: "twitter:url", content: url });
this.metaService.updateTag({
name: "twitter:image",
content: data.image || this.FALLBACK_IMAGE,
});
// Update canonical URL if provided
if (canonicalPath) {
this.updateCanonicalUrl(url);
}
// Set local business schema.org metadata
this.setLocalBusinessSchema();
}
/**
* Updates the canonical URL for the given page.
*
* @param url - The new canonical URL.
*/
private updateCanonicalUrl(url: string) {
let link: HTMLLinkElement =
this.document.querySelector("link[rel='canonical']") ||
this.renderer.createElement("link");
this.renderer.setAttribute(link, "rel", "canonical");
this.renderer.setAttribute(link, "href", url);
if (!this.document.head.contains(link)) {
this.renderer.appendChild(this.document.head, link);
}
}
/**
* Sets the local business schema.org JSON-LD script in the head of the document.
*/
private setLocalBusinessSchema() {
const oldScript = this.document.getElementById('schema-org-data');
if (oldScript) this.renderer.removeChild(this.document.head, oldScript);
const schema = {
"@context": "https://schema.org",
"@type": "WebDesignService",
"name": this.BRAND,
"url": "https://hurler-webdesign.de",
"logo": "https://hurler-webdesign.de/assets/logo.png",
"image": "https://hurler-webdesign.de/assets/office.jpg",
"description": "Spezialist für schnelle Webseiten ohne WordPress für Handwerk & Vereine. Wir bieten maßgeschneidertes Webdesign für eine schnelle und sichere Online-Präsenz.",
"address": {
"@type": "PostalAddress",
"streetAddress": "Untermagerbein 30",
"addressLocality": "Mönchsdeggingen",
"postalCode": "86751",
"addressCountry": "DE"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 48.7506,
"longitude": 10.5773
},
"areaServed": ["Nördlingen", "Donauwörth", "Augsburg", "Bayern"],
"telephone": "+49 171 8084830",
"priceRange": "€€"
};
const script = this.renderer.createElement('script');
script.type = 'application/ld+json';
script.id = 'schema-org-data';
script.text = JSON.stringify(schema);
this.renderer.appendChild(this.document.head, script);
}
}
| 1 | import { Injectable, inject } from "@angular/core"; |
| 2 | import { Title, Meta } from "@angular/platform-browser"; |
| 3 | import { DOCUMENT } from "@angular/common"; |
| 4 | import { RendererFactory2 } from "@angular/core"; |
| 5 | import { SeoData } from "@core/models/seo.model"; |
| 6 | |
| 7 | @Injectable({ providedIn: "root" }) |
| 8 | export class SeoService { |
| 9 | private titleService = inject(Title); |
| 10 | private metaService = inject(Meta); |
| 11 | private document = inject(DOCUMENT); |
| 12 | private rendererFactory = inject(RendererFactory2); |
| 13 | private renderer = |
| 14 | this.rendererFactory.createRenderer(null, null); |
| 15 | |
| 16 | // Constants for metadata |
| 17 | private readonly BRAND = "Hurler Webdesign"; |
| 18 | private readonly FALLBACK_IMAGE = |
| 19 | 'https://hurler-webdesign.de/assets/og-default.jpg'; |
| 20 | private readonly DEFAULT_TYPE = "website"; |
| 21 | |
| 22 | /** |
| 23 | * Updates SEO metadata with the provided data. |
| 24 | * |
| 25 | * @param data - The SeoData object containing title, description, type, and image. |
| 26 | * @param canonicalPath - Optional parameter for the canonical URL path. |
| 27 | */ |
| 28 | updateMetadata(data: SeoData, canonicalPath?: string) { |
| 29 | const fullTitle = `${data.title} | ${this.BRAND}`; |
| 30 | const url = `https://hurler-webdesign.de${canonicalPath || ""}`; |
| 31 | |
| 32 | // Update title and meta description |
| 33 | this.titleService.setTitle(fullTitle); |
| 34 | this.metaService.updateTag({ name: "description", content: data.description }); |
| 35 | |
| 36 | // Open Graph metadata |
| 37 | this.metaService.updateTag({ |
| 38 | property: "og:title", |
| 39 | content: fullTitle, |
| 40 | }); |
| 41 | this.metaService.updateTag({ |
| 42 | property: "og:description", |
| 43 | content: data.description, |
| 44 | }); |
| 45 | this.metaService.updateTag({ |
| 46 | property: "og:type", |
| 47 | content: data.type || this.DEFAULT_TYPE, |
| 48 | }); |
| 49 | this.metaService.updateTag({ |
| 50 | property: "og:image", |
| 51 | content: data.image || this.FALLBACK_IMAGE, |
| 52 | }); |
| 53 | this.metaService.updateTag({ property: "og:url", content: url }); |
| 54 | |
| 55 | // Twitter card metadata |
| 56 | this.metaService.updateTag({ |
| 57 | name: "twitter:card", |
| 58 | content: "summary_large_image", |
| 59 | }); |
| 60 | this.metaService.updateTag({ |
| 61 | name: "twitter:title", |
| 62 | content: fullTitle, |
| 63 | }); |
| 64 | this.metaService.updateTag({ |
| 65 | name: "twitter:description", |
| 66 | content: data.socialsDescription || data.description, |
| 67 | }); |
| 68 | this.metaService.updateTag({ name: "twitter:url", content: url }); |
| 69 | this.metaService.updateTag({ |
| 70 | name: "twitter:image", |
| 71 | content: data.image || this.FALLBACK_IMAGE, |
| 72 | }); |
| 73 | |
| 74 | // Update canonical URL if provided |
| 75 | if (canonicalPath) { |
| 76 | this.updateCanonicalUrl(url); |
| 77 | } |
| 78 | |
| 79 | // Set local business schema.org metadata |
| 80 | this.setLocalBusinessSchema(); |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * Updates the canonical URL for the given page. |
| 85 | * |
| 86 | * @param url - The new canonical URL. |
| 87 | */ |
| 88 | private updateCanonicalUrl(url: string) { |
| 89 | let link: HTMLLinkElement = |
| 90 | this.document.querySelector("link[rel='canonical']") || |
| 91 | this.renderer.createElement("link"); |
| 92 | this.renderer.setAttribute(link, "rel", "canonical"); |
| 93 | this.renderer.setAttribute(link, "href", url); |
| 94 | if (!this.document.head.contains(link)) { |
| 95 | this.renderer.appendChild(this.document.head, link); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * Sets the local business schema.org JSON-LD script in the head of the document. |
| 101 | */ |
| 102 | private setLocalBusinessSchema() { |
| 103 | const oldScript = this.document.getElementById('schema-org-data'); |
| 104 | if (oldScript) this.renderer.removeChild(this.document.head, oldScript); |
| 105 | |
| 106 | const schema = { |
| 107 | "@context": "https://schema.org", |
| 108 | "@type": "WebDesignService", |
| 109 | "name": this.BRAND, |
| 110 | "url": "https://hurler-webdesign.de", |
| 111 | "logo": "https://hurler-webdesign.de/assets/logo.png", |
| 112 | "image": "https://hurler-webdesign.de/assets/office.jpg", |
| 113 | "description": "Spezialist für schnelle Webseiten ohne WordPress für Handwerk & Vereine. Wir bieten maßgeschneidertes Webdesign für eine schnelle und sichere Online-Präsenz.", |
| 114 | "address": { |
| 115 | "@type": "PostalAddress", |
| 116 | "streetAddress": "Untermagerbein 30", |
| 117 | "addressLocality": "Mönchsdeggingen", |
| 118 | "postalCode": "86751", |
| 119 | "addressCountry": "DE" |
| 120 | }, |
| 121 | "geo": { |
| 122 | "@type": "GeoCoordinates", |
| 123 | "latitude": 48.7506, |
| 124 | "longitude": 10.5773 |
| 125 | }, |
| 126 | "areaServed": ["Nördlingen", "Donauwörth", "Augsburg", "Bayern"], |
| 127 | "telephone": "+49 171 8084830", |
| 128 | "priceRange": "€€" |
| 129 | }; |
| 130 | const script = this.renderer.createElement('script'); |
| 131 | script.type = 'application/ld+json'; |
| 132 | script.id = 'schema-org-data'; |
| 133 | script.text = JSON.stringify(schema); |
| 134 | this.renderer.appendChild(this.document.head, script); |
| 135 | } |
| 136 | } |