Geolocalización y APIs: Creando un Mapa de Tiendas Cercanas
Clase 25 de 36 • Curso de Angular Avanzado
Resumen
La geolocalización es una herramienta poderosa que permite a las aplicaciones web ofrecer experiencias personalizadas basadas en la ubicación del usuario. Mediante el uso de APIs nativas del navegador y servicios externos, podemos crear funcionalidades que muestren información relevante según la posición geográfica del visitante. En este artículo, exploraremos cómo implementar la geolocalización en una aplicación Angular para mostrar puntos de interés cercanos a la ubicación del usuario, una característica común en sitios de eCommerce que desean mostrar sus tiendas físicas.
¿Cómo funciona la API de geolocalización del navegador?
La API Navigator.geolocation es una interfaz nativa de los navegadores modernos que permite acceder a la ubicación geográfica del dispositivo. Esta API solo funciona del lado del cliente (browser) y requiere el consentimiento explícito del usuario para compartir su ubicación.
Para utilizar esta API en Angular, debemos asegurarnos de ejecutar el código relacionado con la geolocalización después de que el componente se haya renderizado en el navegador:
import { Component, Signal, signal } from '@angular/core';
import { afterNextRender } from '@angular/core';
@Component({
selector: 'app-locations',
templateUrl: './locations.component.html'
})
export class LocationsComponent {
origin: Signal<string> = signal('');
constructor() {
afterNextRender(() => {
navigator.geolocation.getCurrentPosition((position) => {
const newOrigin = `${position.coords.latitude},${position.coords.longitude}`;
this.origin.set(newOrigin);
});
});
}
}
El método getCurrentPosition()
devuelve un objeto con las coordenadas del usuario, que podemos formatear según nuestras necesidades. En este caso, creamos un string con el formato "latitud,longitud" que utilizaremos para consultar una API externa.
¿Por qué usar afterNextRender?
Es crucial utilizar afterNextRender
cuando trabajamos con APIs que solo están disponibles en el navegador, ya que Angular utiliza renderizado universal (server-side rendering) para la primera carga. Esto garantiza que el código de geolocalización solo se ejecute cuando el componente esté completamente renderizado en el cliente.
¿Cómo consumir una API de localización con las coordenadas del usuario?
Una vez obtenida la ubicación del usuario, podemos utilizarla para consultar una API que nos proporcione puntos de interés cercanos. En nuestro ejemplo, utilizaremos un endpoint de la API de Platzi que acepta parámetros como:
- origin: Coordenadas de origen en formato "latitud,longitud"
- size: Cantidad de puntos a devolver
- radius: Radio de búsqueda (en kilómetros)
Implementación del consumo de la API
async locationResource(request: { origin?: string } = {}) {
const url = new URL(`${environment.API_URL}/api/v1/localizations`);
if (request.origin) {
url.searchParams.set('origin', request.origin);
}
const response = await fetch(url.toString());
return await response.json();
}
Este método recibe un objeto con parámetros opcionales y construye la URL de la petición utilizando la API URL, que nos permite agregar query params de forma sencilla. Si existe un origen, lo añadimos como parámetro a la URL.
Renderizando los resultados en el componente
Para mostrar los puntos de interés en nuestra interfaz, podemos utilizar un bucle en nuestro template:
<ul>
@for (location of locations; track location.id) {
<li>{{ location.name }}</li>
}
</ul>
¿Cómo mejorar la arquitectura de nuestra aplicación?
Aunque el código anterior funciona correctamente, no sigue las mejores prácticas de Angular. Es recomendable separar la lógica de negocio (obtención de datos) de la presentación (componente) siguiendo el principio de responsabilidad única de SOLID.
Creando un servicio dedicado
La forma correcta de implementar esta funcionalidad sería crear un servicio dedicado:
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root'
})
export class LocationsService {
async getLocations(params: { origin?: string, size?: number, radius?: number } = {}) {
const url = new URL(`${environment.API_URL}/api/v1/localizations`);
// Añadir parámetros si existen
if (params.origin) url.searchParams.set('origin', params.origin);
if (params.size) url.searchParams.set('size', params.size.toString());
if (params.radius) url.searchParams.set('radius', params.radius.toString());
const response = await fetch(url.toString());
return await response.json();
}
async getCurrentPosition(): Promise<string> {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
(position) => {
const coords = `${position.coords.latitude},${position.coords.longitude}`;
resolve(coords);
},
(error) => {
reject(error);
}
);
});
}
}
Este servicio encapsula toda la lógica relacionada con la obtención de la ubicación del usuario y la consulta a la API de localización, lo que hace que nuestro componente sea más limpio y mantenible.
Utilizando el servicio en el componente
import { Component, Signal, signal } from '@angular/core';
import { afterNextRender } from '@angular/core';
import { LocationsService } from './locations.service';
@Component({
selector: 'app-locations',
templateUrl: './locations.component.html'
})
export class LocationsComponent {
locations: Signal<any[]> = signal([]);
constructor(private locationsService: LocationsService) {
// Cargar ubicaciones por defecto
this.loadLocations();
// Cargar ubicaciones basadas en la posición del usuario
afterNextRender(async () => {
try {
const origin = await this.locationsService.getCurrentPosition();
this.loadLocations({ origin });
} catch (error) {
console.error('Error obteniendo la ubicación:', error);
}
});
}
async loadLocations(params = {}) {
const data = await this.locationsService.getLocations(params);
this.locations.set(data);
}
}
Esta implementación es más robusta y sigue las mejores prácticas de Angular, separando claramente las responsabilidades entre el componente y el servicio.
La geolocalización es una herramienta poderosa que puede mejorar significativamente la experiencia de usuario en aplicaciones web. Al implementarla correctamente en Angular, podemos ofrecer contenido personalizado basado en la ubicación del usuario de manera eficiente y mantenible. ¿Has implementado funcionalidades de geolocalización en tus proyectos? Comparte tu experiencia en los comentarios.