When navigating to this route user/:username I load the user data from a service to render it, but when updating the metatags with that data for page's SEO, it does not update it at the server just the client.
user.page.ts
ngOnInit(): void {
this.route.params.subscribe( (params: ProfileParams) => this.getUserProfile(params.username) );
}
private getUserProfile(username: string): void {
this.profiles.getUserProfile(username).subscribe({
next: profile => {
this.profile = profile;
this.updateMetas();
}, error: error => {
this.router.navigate(['404']);
console.error(error);
}
});
private updateMetas(): void {
const PROFILE_NAME: string = `${this.profile?.name || 'Profile'} ${this.profile?.last_name || ''}`.trim();
this.title.setTitle(`${PROFILE_NAME} | ${environment.appName}`);
this.seo.updateUrlMetas({
title: PROFILE_NAME,
description: this.profile?.description,
image: this.profile?.profile_picture,
keywords: [PROFILE_NAME, this.profile?.profession, this.profile?.locale]
});
}
seo.service.ts
private platformId = inject(PLATFORM_ID);
constructor(
private title: Title,
private meta: Meta,
private router: Router
) {}
private sanitizeText(text?: string): string {
return text ? text.replace(/(\r\n|\n|\r)/gm, ' ') : '';
}
public updateUrlMetas(seoData: SeoData): void {
//this.title.setTitle(seoData.title);
this.meta.updateTag({ name: 'title', content: seoData.title }, `name='title'`);
this.meta.updateTag({ property: 'og:title', content: seoData.title }, `property='og:title'`);
this.meta.updateTag({ name: 'twitter:title', content: seoData.title }, `name='twitter:title'`);
if (seoData.description) {
const cleanDescription = this.sanitizeText(seoData.description);
this.meta.updateTag({ name: 'description', content: cleanDescription }, `name='description'`);
this.meta.updateTag({ property: 'og:description', content: cleanDescription }, `property='og:description'`);
this.meta.updateTag({ name: 'twitter:description', content: cleanDescription }, `name='twitter:description'`);
}
if (seoData.image) {
this.meta.updateTag({ property: 'og:image', content: seoData.image }, `property='og:image'`);
this.meta.updateTag({ name: 'twitter:image', content: seoData.image }, `name='twitter:image'`);
}
if (seoData.keywords?.length) {
this.meta.updateTag({ name: 'keywords', content: seoData.keywords.join(', ') }, `name='keywords'`);
}
const fullUrl = isPlatformServer(this.platformId)
? `https://konfii.com${this.router.url}`
: window.location.href;
this.meta.updateTag({ property: 'og:url', content: fullUrl }, `property='og:url'`);
this.meta.updateTag({ property: 'al:web:url', content: fullUrl }, `property='al:web:url'`);
this.meta.updateTag({ property: 'al:android:url', content: `com.konfii.app:${this.router.url}` }, `property='al:android:url'`);
this.meta.updateTag({ property: 'og:type', content: 'website' }, `property='og:type'`);
this.meta.updateTag({ name: 'twitter:card', content: seoData.image ? 'summary_large_image' : 'summary' }, `name='twitter:card'`);
}
I have other component using that service function and it works fine, all meta tags update and the SEO aswell.
service-detail.component.ts
ngOnInit(): void {
if(!this.uuid) { this.router.navigate(['404']); }
this.refreshFunction.subscribe( event => this.handleRefresh(event) );
this.services.getService(this.uuid, this.auth?.user?.unique_code).subscribe({
next: res => {
this.serviceDetail = res;
this.setMetaTags(res);
this.reviews.getReviewsPerService(this.uuid, 1).subscribe(reviews => this.serviceReviews = reviews);
this.reviews.getReviewScorePerService(this.uuid).subscribe(score => this.serviceScore = score);
}, error: error => {
console.error(error);
if(error.status === 404) { return this.router.navigate(['404']); }
}
});
}
I'm using Angular 16.1.7, NgUniversal 16.1.1 and Ionic 8.
I eventually discovered that the error was caused because my geolocation service was being called in the constructor of my Profile component without checking whether the code was running on the server or the browser. During server-side rendering, there is no navigator.geolocation
(and no document
or window
), so when the code tried to access getCurrentPosition
, it threw a TypeError.
How I Diagnosed the Issue:
navigator
, document
, or window
) is conditionally executed only on the client side. One reliable way to do this is by injecting PLATFORM_ID
from @angular/core
and using Angular's isPlatformBrowser
function from @angular/common
.Solution:
I updated my localization service so that the geolocation code is only executed when running in the browser. For example:
import { isPlatformBrowser } from '@angular/common';
import { Inject, PLATFORM_ID } from '@angular/core';
export class LocalizationService {
constructor(
@Inject(PLATFORM_ID) private platformId: Object,
private language: LanguageService,
private alert: AlertService
) {}
private getWebLocalization(): void {
if (isPlatformBrowser(this.platformId)) {
navigator.geolocation.getCurrentPosition(
position => this.getReverseGeocodingPosition({
coords: {
latitude: position.coords.latitude,
longitude: position.coords.longitude
}
}),
{ enableHighAccuracy: true }
);
} else {
console.warn('Geolocation is not available on the server.');
}
}
private getReverseGeocodingPosition(data: any): void {
// Reverse geocoding logic...
}
}
Key Takeaways:
Review Your Dependencies:
Always check the services and code executed in your component constructors or lifecycle hooks. Make sure that any functionality depending on browser-specific APIs is guarded by a platform check (using isPlatformBrowser
).
Manual Debugging: If none of the existing solutions on the site work, try reviewing the server console for errors and tracing through your interceptor’s flow (like the ServerStateInterceptor in my case) to pinpoint where the browser-only code is being executed on the server.
This approach ensured that the geolocation code only runs on the client, preventing the “document is not defined” or “getCurrentPosition” errors on the server side.