Last active 1 day ago

Revision f5c43954651934a78bfc1bd40450d9caf8e402e2

seo.service.ts Raw
1import { Injectable, inject } from "@angular/core";
2import { Title, Meta } from "@angular/platform-browser";
3import { DOCUMENT } from "@angular/common";
4import { RendererFactory2 } from "@angular/core";
5import { SeoData } from "@core/models/seo.model";
6
7@Injectable({ providedIn: "root" })
8export 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}