En esta charla, Adam habló sobre el uso de Scoped Slots (disponibles a partir de Vue +2.1.0) para la construcción avanzada de componentes con Vue.js. En este post te daremos un resumen de la charla de Adam para Laracon Online 2018.
El problema de proveer un componente (open source) que otros puedan descargar y utilizar, es que cada aplicación tiene un diseño o requerimientos de diseño diferentes. Muchas veces necesitamos un componente y encontramos uno que provee la funcionalidad que necesitamos, pero el diseño es completamente diferente al requerido.
Permitir la personalización del look & feel de un componente de Vue
Hay varias maneras en que el creador de un componente de Vue puede permitir la personalización del estilo del mismo a quienes quieran descargar e integrar dicho componente:
Agregar propiedades al componente:
Si quieres permitir la personalización de color de fondo o de los bordes, como autor de un componente, podrías permitir que el usuario pase las siguientes propiedades y usarlas luego:
<!-- ejemplo con propiedades para estilos: --> <my-component bg-color="blue" border-color="black">Ejemplo</my-component>
La desventaja de esto es que el uso del componente se puede volver algo complejo, podrían existir demasiadas opciones de configuración, las cuales por supuesto requerirían de mucha documentación y aún así es posible que no cubras todos los escenarios posibles.
Proveer clases en el HTML
Sobre todo en época de jQuery, cuando instalábamos un componente, revisábamos el HTML generado para ver qué clases usaba el componente y poder personalizar los estilos con CSS:
<!-- Ejemplo con clases de CSS personalizadas dentro del HTML del componente: --> <p class="component-paragraph"><span class="component-span">Demo</span></p>
Desventaja: de nuevo requiere de mucha personalización de CSS, pueden haber colisiones de clases, entre otros problemas.
Pasar clases al componente:
Tal vez podríamos permitir que el usuario pase sus propias clases como propiedades del componente y aplicarlas luego:
<!-- Permitir el paso de clases personalizadas: --> <my-component bg-color-class="my-custom-class"></my-component>
Esto evitaría posibles colisiones de clases, pero de todas formas la personalización será limitada a lo que ofrece el componente. Por esto, no sería posible proveer todas las opciones de personalización que el usuario podría necesitar.
Una solución a este problema es el uso de Scoped Slots, disponibles a partir de la versión 2.1 de Vue.js:
Uso de Scoped Slots en Vue
Le permiten al usuario pasar HTML personalizado al componente. Los slots son como propiedades del componente, pero en vez de pasar un valor o un callback, vamos a pasar HTML a nuestro componente y de hecho vamos a recibir valores y métodos que podemos aplicar a nuestro HTML personalizado. Veamos un ejemplo:
Imaginemos que queremos crear un componente para mostrar un listado de tags, llamado TagsInput, en vez de colocar el HTML para imprimir un tag directamente en el componente y proveer doscientas opciones de configuración, vamos a definir un slot y a permitirle a quien consuma el componente escribir un HTML personalizado para imprimir un tag:
En la declaración del componente: /components/TagsInput.vue
<!-- En la declaración del componente: /components/TagsInput.vue --> <template> <div class="tags-input"> <slot name="tag" v-for="tag in tags" :tag="tag" :removeTag="removeTag"></slot> <input class="tags-input-text" placeholder="Add tag.." v-model="newTag"> </div> </template>
Cuando necesitemos usar el component tags-input
<!-- Cuando necesitemos usar el component tags-input --> <tags-input v-model="tags"> <span slot="tag" slot-scope="{ tag, removeTag }" class="tags-input-tag"> <span>{{ tag }}</span> <button type="button" class="tags-input-remove" @click="removeTag(tag)"> × </button> </span> </tags-input>
Como puedes ver, el HTML para imprimir el tag (la etiqueta span
, el botón para remover un tag, etc.) se encuentra en el lugar donde se usa el componente (código de abajo) y no donde se declara el componente (código de arriba). Algo muy importante es cómo se pasa el objeto tag
y el callback removeTag
a través de la etiqueta slot
en la declaración del componente y luego, en el uso del componente, se extrae esta información utilizando el atributo slot-scope
de la siguiente forma: slot-scope="{ tag, removeTag }"
.
Esto permite crear componentes que sean mucho más portables en cuenta a diseño e incluso que no requieran HTML en lo absoluto, si se usan Render Functions (como aprendimos en la charla de Evan You en Laracon Online 2017).
Si quieres aprender más sobre este tema, por favor visita el sitio oficial de Laracon, sigue a Adam en Twitter y si quieres aprender más sobre Vue en general, revisa nuestro Curso de Vue 2 e inscríbete a nuestro mail list, te enviaremos un email cuando publiquemos nuevas lecciones de Vue:
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.