Schema.org
This commit is contained in:
parent
70a50e0ff6
commit
6f1109d593
|
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable, inject, PLATFORM_ID } from '@angular/core';
|
import { Injectable, inject, PLATFORM_ID, Renderer2, RendererFactory2 } from '@angular/core';
|
||||||
import { isPlatformBrowser } from '@angular/common';
|
import { isPlatformBrowser, DOCUMENT } from '@angular/common';
|
||||||
import { Meta, Title } from '@angular/platform-browser';
|
import { Meta, Title } from '@angular/platform-browser';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
|
@ -22,11 +22,17 @@ export class SeoService {
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
private platformId = inject(PLATFORM_ID);
|
private platformId = inject(PLATFORM_ID);
|
||||||
private isBrowser = isPlatformBrowser(this.platformId);
|
private isBrowser = isPlatformBrowser(this.platformId);
|
||||||
|
private document = inject(DOCUMENT);
|
||||||
|
private renderer: Renderer2;
|
||||||
|
|
||||||
private readonly defaultImage = 'https://www.bizmatch.net/assets/images/bizmatch-og-image.jpg';
|
private readonly defaultImage = 'https://www.bizmatch.net/assets/images/bizmatch-og-image.jpg';
|
||||||
private readonly siteName = 'BizMatch';
|
private readonly siteName = 'BizMatch';
|
||||||
private readonly baseUrl = 'https://www.bizmatch.net';
|
private readonly baseUrl = 'https://www.bizmatch.net';
|
||||||
|
|
||||||
|
constructor(rendererFactory: RendererFactory2) {
|
||||||
|
this.renderer = rendererFactory.createRenderer(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base URL for SEO purposes
|
* Get the base URL for SEO purposes
|
||||||
*/
|
*/
|
||||||
|
|
@ -109,20 +115,18 @@ export class SeoService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update canonical URL
|
* Update canonical URL (SSR-compatible using Renderer2)
|
||||||
*/
|
*/
|
||||||
private updateCanonicalUrl(url: string): void {
|
private updateCanonicalUrl(url: string): void {
|
||||||
if (!this.isBrowser) return;
|
let link: HTMLLinkElement | null = this.document.querySelector('link[rel="canonical"]');
|
||||||
|
|
||||||
let link: HTMLLinkElement | null = document.querySelector('link[rel="canonical"]');
|
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
link.setAttribute('href', url);
|
this.renderer.setAttribute(link, 'href', url);
|
||||||
} else {
|
} else {
|
||||||
link = document.createElement('link');
|
link = this.renderer.createElement('link');
|
||||||
link.setAttribute('rel', 'canonical');
|
this.renderer.setAttribute(link, 'rel', 'canonical');
|
||||||
link.setAttribute('href', url);
|
this.renderer.setAttribute(link, 'href', url);
|
||||||
document.head.appendChild(link);
|
this.renderer.appendChild(this.document.head, link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,32 +273,40 @@ export class SeoService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject JSON-LD structured data into page
|
* Inject JSON-LD structured data into page (SSR-compatible using Renderer2)
|
||||||
*/
|
*/
|
||||||
injectStructuredData(schema: object): void {
|
injectStructuredData(schema: object): void {
|
||||||
if (!this.isBrowser) return;
|
// Clear existing schema scripts with the same type
|
||||||
|
this.removeAllSchemas();
|
||||||
|
|
||||||
// Remove existing schema script
|
// Create new script element using Renderer2 (works in both SSR and browser)
|
||||||
const existingScript = document.querySelector('script[type="application/ld+json"]');
|
const script = this.renderer.createElement('script');
|
||||||
if (existingScript) {
|
this.renderer.setAttribute(script, 'type', 'application/ld+json');
|
||||||
existingScript.remove();
|
this.renderer.setAttribute(script, 'data-schema', 'true');
|
||||||
}
|
|
||||||
|
|
||||||
// Add new schema script
|
// Create text node with schema JSON
|
||||||
const script = document.createElement('script');
|
const schemaText = this.renderer.createText(JSON.stringify(schema));
|
||||||
script.type = 'application/ld+json';
|
this.renderer.appendChild(script, schemaText);
|
||||||
script.text = JSON.stringify(schema);
|
|
||||||
document.head.appendChild(script);
|
// Append to document head
|
||||||
|
this.renderer.appendChild(this.document.head, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all structured data
|
* Remove all schema scripts (internal helper, SSR-compatible)
|
||||||
|
*/
|
||||||
|
private removeAllSchemas(): void {
|
||||||
|
const existingScripts = this.document.querySelectorAll('script[data-schema="true"]');
|
||||||
|
existingScripts.forEach(script => {
|
||||||
|
this.renderer.removeChild(this.document.head, script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all structured data (SSR-compatible)
|
||||||
*/
|
*/
|
||||||
clearStructuredData(): void {
|
clearStructuredData(): void {
|
||||||
if (!this.isBrowser) return;
|
this.removeAllSchemas();
|
||||||
|
|
||||||
const scripts = document.querySelectorAll('script[type="application/ld+json"]');
|
|
||||||
scripts.forEach(script => script.remove());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -516,20 +528,21 @@ export class SeoService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject multiple structured data schemas
|
* Inject multiple structured data schemas (SSR-compatible using Renderer2)
|
||||||
*/
|
*/
|
||||||
injectMultipleSchemas(schemas: object[]): void {
|
injectMultipleSchemas(schemas: object[]): void {
|
||||||
if (!this.isBrowser) return;
|
// Clear existing schema scripts
|
||||||
|
this.removeAllSchemas();
|
||||||
|
|
||||||
// Remove existing schema scripts
|
// Add new schema scripts using Renderer2
|
||||||
this.clearStructuredData();
|
|
||||||
|
|
||||||
// Add new schema scripts
|
|
||||||
schemas.forEach(schema => {
|
schemas.forEach(schema => {
|
||||||
const script = document.createElement('script');
|
const script = this.renderer.createElement('script');
|
||||||
script.type = 'application/ld+json';
|
this.renderer.setAttribute(script, 'type', 'application/ld+json');
|
||||||
script.text = JSON.stringify(schema);
|
this.renderer.setAttribute(script, 'data-schema', 'true');
|
||||||
document.head.appendChild(script);
|
|
||||||
|
const schemaText = this.renderer.createText(JSON.stringify(schema));
|
||||||
|
this.renderer.appendChild(script, schemaText);
|
||||||
|
this.renderer.appendChild(this.document.head, script);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,25 @@
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
// Development: use browser-bunyan for rich logging
|
// Lightweight logger implementation for both dev and production
|
||||||
// Production: use lightweight console wrapper to avoid loading 50KB+ library
|
// Avoids dynamic require() which causes build issues
|
||||||
export const createLogger = environment.production
|
const createLoggerImpl = (name: string) => ({
|
||||||
? (name: string) => ({
|
info: (...args: any[]) => {
|
||||||
info: (...args: any[]) => console.log(`[${name}]`, ...args),
|
if (!environment.production) {
|
||||||
|
console.log(`[${name}]`, ...args);
|
||||||
|
}
|
||||||
|
},
|
||||||
warn: (...args: any[]) => console.warn(`[${name}]`, ...args),
|
warn: (...args: any[]) => console.warn(`[${name}]`, ...args),
|
||||||
error: (...args: any[]) => console.error(`[${name}]`, ...args),
|
error: (...args: any[]) => console.error(`[${name}]`, ...args),
|
||||||
debug: () => {}, // no-op in production
|
debug: (...args: any[]) => {
|
||||||
})
|
if (!environment.production) {
|
||||||
: require('browser-bunyan').createLogger;
|
console.debug(`[${name}]`, ...args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trace: (...args: any[]) => {
|
||||||
|
if (!environment.production) {
|
||||||
|
console.trace(`[${name}]`, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createLogger = createLoggerImpl;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Build information, automatically generated by `the_build_script` :zwinkern:
|
// Build information, automatically generated by `the_build_script` :zwinkern:
|
||||||
const build = {
|
const build = {
|
||||||
timestamp: "GER: 04.02.2026 21:20 | TX: 02/04/2026 2:20 PM"
|
timestamp: "GER: 05.02.2026 12:45 | TX: 02/05/2026 5:45 AM"
|
||||||
};
|
};
|
||||||
|
|
||||||
export default build;
|
export default build;
|
||||||
|
|
@ -61,6 +61,53 @@
|
||||||
<link rel="icon" href="/assets/cropped-Favicon-32x32.png" sizes="32x32" />
|
<link rel="icon" href="/assets/cropped-Favicon-32x32.png" sizes="32x32" />
|
||||||
<link rel="icon" href="/assets/cropped-Favicon-192x192.png" sizes="192x192" />
|
<link rel="icon" href="/assets/cropped-Favicon-192x192.png" sizes="192x192" />
|
||||||
<link rel="apple-touch-icon" href="/assets/cropped-Favicon-180x180.png" />
|
<link rel="apple-touch-icon" href="/assets/cropped-Favicon-180x180.png" />
|
||||||
|
|
||||||
|
<!-- Schema.org Structured Data (Static) -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "BizMatch",
|
||||||
|
"url": "https://www.bizmatch.net",
|
||||||
|
"logo": "https://www.bizmatch.net/assets/images/bizmatch-logo.png",
|
||||||
|
"description": "Buy and sell businesses, commercial properties, and franchises. Browse thousands of verified listings across the United States.",
|
||||||
|
"address": {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
"streetAddress": "1001 Blucher Street",
|
||||||
|
"addressLocality": "Corpus Christi",
|
||||||
|
"addressRegion": "TX",
|
||||||
|
"postalCode": "78401",
|
||||||
|
"addressCountry": "US"
|
||||||
|
},
|
||||||
|
"contactPoint": {
|
||||||
|
"@type": "ContactPoint",
|
||||||
|
"contactType": "Customer Service",
|
||||||
|
"email": "info@bizmatch.net"
|
||||||
|
},
|
||||||
|
"sameAs": [
|
||||||
|
"https://www.facebook.com/bizmatch",
|
||||||
|
"https://www.linkedin.com/company/bizmatch",
|
||||||
|
"https://twitter.com/bizmatch"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebSite",
|
||||||
|
"name": "BizMatch",
|
||||||
|
"url": "https://www.bizmatch.net",
|
||||||
|
"potentialAction": {
|
||||||
|
"@type": "SearchAction",
|
||||||
|
"target": {
|
||||||
|
"@type": "EntryPoint",
|
||||||
|
"urlTemplate": "https://www.bizmatch.net/businessListings?search={search_term_string}"
|
||||||
|
},
|
||||||
|
"query-input": "required name=search_term_string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="flex flex-col min-h-screen">
|
<body class="flex flex-col min-h-screen">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue