- Aprender
-
Ecosistema
Ayuda
Herramientas
Librerías Oficiales
Noticias
Lista de Recursos
- Equipo
- Apoyar Vue
- Traducciones
Libro de Recetasbeta
- Introducción
- Agregando Propiedades de Instancia
- Validación de Formularios
- Sistema de Íconos SVG Editables
- Crear un Blog en base a un CMS
- Pruebas unitarias de componentes Vue
- Creando Directivas de Desplazamiento
- Depurando en VS Code
- Usando Axios para consumir APIs
- Evitando las fugas de memoria
- Almacenamiento del lado del cliente
- Paquete de Componentes de Vue para npm
- Dockerizar una aplicación Vue.js
- Uso práctico de scoped slots con GoogleMaps
Uso práctico de scoped slots con GoogleMaps
Ejemplo Base
Hay situaciones en las que usted querrá que el template dentro del slot acceda a los datos del componente hijo, que es responsable de renderizar el contenido del slot. Esto es particularmente útil cuando usted necesita libertad al crear templates personalizados que utilizen los datos y propiedades del componente hijo. Este es un caso típico para el uso de scoped slots.
Imagine un componente que configura y prepara una API externa para ser usada en otro componente, pero no está fuertemente acoplada a un template específico. Tal componente puede ser reusado en muchos lugares, renderizando diferentes templates pero usando el mismo objeto con la API específica.
Crearemos un componente (GoogleMapLoader.vue
) que:
- Inicializará la API de Google Maps
- Creará los objetos
google
ymap
- Expondrá estos objetos a su componente padre, donde
GoogleMapLoader
es utilizado
Debajo hay un ejemplo de cómo se puede lograr esto. En la siguiente sección, analizaremos el código pieza por pieza y veremos qué es lo que realmente está sucediendo.
Pero, primero, declaramos el template para GoogleMapLoader.vue
:
<template> |
Ahora, nuestro script necesita pasar algunas props al componente que nos permitan setear la API de Google Maps y el objeto Map:
import GoogleMapsApiLoader from 'google-maps-api-loader' |
Este es simplemente un ejemplo funcional, usted puede encontrar el ejemplo completo abajo.
Ejemplo en el mundo real: creando un componente que inicialize Google Map
1. Crear un componente que inicialize nuestro mapa
GoogleMapLoader.vue
En este template, creamos un contenedor para el mapa, que será utilizado para montar el objeto Map extraído desde la API de Google Maps.
<template> |
Luego, nuestro script necesita recibir props desde su componente padre, esto nos permitirá setear el Google Map. Estas props consisten en:
- mapConfig: objeto de configuración de Google Maps
- apiKey: nuestra clave personal requerida por la API de Google Maps
import GoogleMapsApiLoader from 'google-maps-api-loader' |
Después, inicializamos google y map como null:
data() { |
En el método mounted
instanciemos los objetos googleMapApi
y Map
desde googleMapsApi
y asignemos nuestras propiedades google
y map
con los de la instancia creada:
async mounted() { |
Hasta ahora todo bien. Podríamos continuar añandiendo objetos al mapa (Marcadores, Líneas Polígonales, etc.) y usarlo como si fuera un componente ordinario.
Pero lo que queremos es usar nuestro componente GoogleMapLoader
solamente como un inicializador que prepara el mapa - no queremos renderizar nada en él.
Para lograrlo, tenemos que permitir que el componente padre que utilizará GoogleMapLoader
acceda a this.google
y this.map
, que se encuentran definidos dentro de nuestro componente GoogleMapLoader
. Aquí es donde los scoped slots realmente brillan. Los scoped slots nos permiten exponer al componente padre propiedades definidas en el componente hijo. Esto podria parecer magia, pero no lo es; a continuación veremos como funciona.
2. Crear un componente que use nuestro componente inicializador
TravelMap.vue
En el template, renderizamos el componente GoogleMapLoader
y le pasamos las props requeridas para inicializar el mapa.
<template> |
Nuestro script tag se verá de la siguiente forma:
<script> |
Aun no hay scoped slots, así que añadamos uno.
3. Exponer las propiedades google
y map
a el componente padre añadiendo un scoped slot
Finalmente, podemos añadir un scoped slot que nos permitirá acceder a las propiedades del componente hijo desde el componente padre. Logramos esto añadiendo una tag <slot>
en el componente hijo y pasándole las props que queremos exponer (usando la directiva v-bind
o el modo abreviado :propName
). Es equivalente a pasar props a un componente hijo, pero realizándolo en el tag <slot>
revertimos la dirección del flujo de información.
GoogleMapLoader.vue
<template> |
Ahora, cuando tenemos el slot en el componente hijo, en el componente padre tenemos que recibir y consumir las propiedades expuestas.
4. Recibir en el componente padre las propiedades expuestas, utilizando el atributo slot-scope
Para recibir las propiedad en el componente padre, declaramos un template y utilizamos el atributo slot-scope
. Este atributo tiene acceso al objeto que contiene todas las propiedades expuestas por el componente hijo. Podemos acceder a todo el objeto o podemos desestructurar este objeto y solo acceder a lo que necesitamos.
Desestructurémoslo y obtengamos lo que necesitamos.
TravelMap.vue
<GoogleMapLoader |
Aunque las propiedades google
y map
no existen en el el scope de TravelMap
, el componente tiene acceso a ellas y pueden ser usadas en el template.
Puede estar pensando ¿por qué haría cosas como esta? y ¿cuál es la utilidad de todo esto?
Los scoped slots nos permiten pasar un template al slot en vez de un elemento renderizado. Se le llama scoped
slot porque tendrá acceso a ciertos datos del componente hijo, aunque este template sea renderizado en el scope del componente padre. Eso nos da la libertad de elegir el contenido del template desde el componente padre.
5. Crear un componente factory para Marcadores y Líneas Poligonales
Ahora que nuestro mapa está pronto, crearemos dos componentes factory que usaremos para añadir elementos a TravelMal
.
GoogleMapMarker.vue
import { POINT_MARKER_ICON_CONFIG } from '@/constants/mapSettings' |
GoogleMapLine.vue
import { LINE_PATH_CONFIG } from '@/constants/mapSettings' |
Ambos reciben la propiedad google
que usamos para extraer el objeto deseado (Marcadores o Líneas Poligonales) así como map
, la cual da referencia al mapa en el que queremos poner nuestro elemento.
Cada componente también acepta una prop extra para crear el elemento correspondiente. En este caso tenemos marker
y path
, respectivamente.
En el método mounted
del ciclo de vida, creamos un element (Marcador/Línea Polígonal) y lo añadimos a nuestro mapa pasando la propiedad map
al constructor del objeto.
Aún queda un paso más por hacer…
6. Añadir elementos al mapa
Utilicemos nuestros componentes factory para añadir elementos al mapa. Debemos renderizar el componente factory y pasarle los objetos google
y map
así la información fluye al lugar correcto.
También debemos proveer la información que requiere el elemento. En nuestro caso, este es el objeto marker
con la posición del marcador y el objeto path
con las coordenadas de la Línea Poligonal.
Integremos la información directamente en el template:
<GoogleMapLoader |
Necesitamos importar los componentes factory en nuestro script y preparar la información que luego le pasaremos a los marcadores y líneas:
import { mapSettings } from '@/constants/mapSettings' |
Cuándo evitar este patrón
Puede parecer muy tentador crear una solución muy compleja basada en este ejemplo, pero en cierto punto podemos llegar a la situación en la que esta abstracción se convierte en una parte independiente de código viviendo en el proyecto. Si llegamos a tal punto, puede valer la pena considerar extraer esta abstracción a un componente que no utilize scoped slots.
Resumiendo
Esto es todo. Ahora podemos reusar el componente GoogleMapLoader
como base para todos nuestros mapas, pasando diferentes templates a cada uno de ellos. Imagine que necesita crear otro mapa con diferentes marcadores o simplemente marcadores sin líneas poligonales. Usando este patrón resulta muy fácil, ya que sólo se requiere pasar un contenido diferente al componente GoogleMapLoader
.
Este patrón no está estrictamente conectado con Google Maps; puede ser utilizado en un componente base con cualquier librería para luego exponer la API de la misma, que luego puede ser utilizada por el componente que invoca dicho componente base.