Qwik — Slots
Aprende cómo proyectar contenido entre componentes utilizando Slots en este tutorial avanzado de Qwik. ¡Domina esta potente funcionalidad!
Comenzamos un nuevo artículo en el que vamos a trabajar con un concepto avanzado que es el de proyectar información de un componente a otro, y esto lo hacemos haciendo uso de los Slots mediante proyección de contenido.
Para trabajar con este artículo os recomiendo que estudiéis los contenidos de los artículos anteriores, sobre todo de los correspondientes a componentes y estilos.
Os recomiendo leer los artículos anteriores en orden y si practicáis, ¡¡mucho mejor!!.
Recordad, que si hay dudas, aun leyéndolo, podéis preguntarlo en los comentarios.
Todos los artículos publicados del curso los encontraréis en la siguiente lista que iré actualizando semanalmente y estableciendo el orden natural recomendado:
Si queréis recibir notificaciones sobre este contenido y similares, os animo a que os suscribáis a mi lista de correo:
Solo habrá avisos con nuevos contenidos, es decir, nuevos artículos (1–3 máximo a la semana, generalmente 1).
El tutorial es compatible con todas las versiones de Qwik hasta el momento. Actualmente (15/06/2023) estamos trabajando con la versión 1.1.5 y esta será con la que desarrolle el código de lo que vamos a encontrar a continuación.
Repositorio con el resultado:
Lo que se verá con los diferentes apartados por ruta:
Apartados que vamos a ver en este artículo
- Introducción a los Slots.
- ¿Por qué
<Slot>
? ¿Por qué Qwik opta por utilizar<Slot>
en lugar de la propiedad children? - Primer ejemplo básico de proyección con
<Slot>
- Analizando el comportamiento de renderización
- Named Slots
- Fallback content (contenido de respaldo)
Introducción a los Slots
Los Slots nos van a permitir que un componente trate a los hijos JSX del componente como una forma de entrada y proyecte estos hijos en el árbol DOM del componente.
Este concepto se llama de distintas formas en estas opciones de ejemplo:
- En Angular se llama Content Projection
.
- En React, son los hijos (children
) de la propiedad props
.
- En Web Components también se utiliza <slot>
.
La API principal para lograr esto es el componente <Slot>
, exportado en @builder.io/qwik
.
Esta proyección dada con el <Slot>
es una colaboración entre el componente padre y el componente hijo.
El componente padre va a decidir qué contenido va a ser renderizado, mientras que el componente hijo tomará la decisión de dónde y si el contenido debe ser renderizado.
Con esta funcionalidad podemos olvidarnos de realizar el proceso de pasar información usando props
, por poner un ejemplo.
¿Por qué <Slot>? ¿Por qué Qwik opta por utilizar <Slot> en lugar de la propiedad children?
El uso de <Slot>
es una elección estratégica para permitir el renderizado de componentes sin un orden específico. (Es decir, que un componente sea capaz de volver a renderizarse incluso si el componente padre aún no se ha reanudado). Hay dos problemas con el uso de children
en Qwik.
Para que Qwik utilice la propiedad children
para la proyección, esta debería ser serializable, al igual que los demás valores que se pasan entre componentes en Qwik.
Un componente hijo podría modificar el contenido de children
antes de insertarlo en el árbol de renderizado. Esto impediría que el componente padre se renderizarse de forma independiente al hijo. Si un hijo modificase children
, tendría que hacerlo cada vez que el componente padre actualice el valor de children
.
Para Qwik, el enfoque de <Slot>
es preferible porque controla de manera declarativa el contenido y la ubicación de la proyección. Esto permitirá al componente padre que cambie el contenido de la proyección sin obligar al componente hijo a volver a renderizarse con lo que conlleva a una mejora de rendimiento considerable.
Que mejor que empezar a plasmar estos conceptos teóricos en aspectos prácticos, para que podamos ver como trabajar con <Slot>
para proyectar la información deseada de padre a hijo
Primer ejemplo básico de proyección con Slot
A continuación os añado un ejemplo, el típico ejemplo sencillo que usaremos para empezar a entender los conceptos de la proyección.
En este caso vamos a especificar desde el padre para que muestre un contenido proyectado en el hijo, que sería inicialmente el siguiente contenido:
Contenido dentro del componente {'<Button>'} marcado con el elemento {`<Slot>`}
Y para hacer posible esa proyección, debemos de añadir el elemento <Slot>
dentro del componente (hijo) al que queremos proyectar esa información.
import { Slot, component$ } from '@builder.io/qwik';
// Aquí tenemos el componente (hijo) al que pasaremos la información a proyectar en "Slot"
const Button = component$(() => {
return (
<button>
Lo que se proyecta: <Slot />
</button>
);
});
Aplicándolo en el ejemplo quedará así:
// 1: Importamos el elemento Slot para proyecciones
import { Slot, component$ } from '@builder.io/qwik';
// 2 Componente (Hijo) donde proyectaremos lo que pasamos desde el padre a "Slot"
const Button = component$(() => {
return (
<button>
Lo que se proyecta: <Slot />
</button>
);
});
// 3 Componente principal que usa "Button" y pasa "Contenido..." para proyectarse dentro de "Slot"
export default component$(() => {
return (
<Button>
Contenido dentro del componente {'<Button>'} marcado con el elemento {`<Slot>`}
</Button>
);
});
En nuestro ejemplo, el contenido del elemento <Button>
(dentro del componente principal default) es el contenido que debe ser proyectado. El componente <Button>
envuelve el contenido deseado y lo proyectará utilizando el elemento <Slot>
.
El resultado debería de ser el siguiente:
Como se puede apreciar, lo que está a la derecha es lo que pasamos al componente <Button>
desde el padre para añadirse en el elemento <Slot>
y proyectarse dinámicamente.
Si aplicamos estas variantes:
- Contenido 2
- Contenido 3 con {‘Button’} probando variantes
- Otra variante
De la siguiente forma:
// 1: Importamos el elemento Slot para proyecciones
import { Slot, component$ } from '@builder.io/qwik';
// 2 Componente donde proyectaremos lo que pasamos desde el padre a "Slot"
...
// 3 Componente principal que usa "Button" y pasa "Contenido..." para proyectarse dentro de "Slot"
export default component$(() => {
return (
<>
<Button>
Contenido dentro del componente {"<Button>"} marcado con el elemento{" "}
{`<Slot>`}
</Button>
<Button>Contenido 2</Button>
<Button>Contenido 3 con {"<Button>"} probando variantes</Button>
<Button>Otra variante</Button>
</>
);
});
Este sería el resultado:
Aquí pasará lo mismo que el primer ejemplo, pero se muestran cuatro botones con el contenido fijo Lo que se proyecta
y posteriormente lo que pasamos al componente <Button>
desde el padre para añadirse en el elemento <Slot>
y así conseguimos que se proyecte ese contenido dinámicamente.
Analizando el comportamiento de renderización
Ahora que ya sabemos como proyectar contenido aunque sea de una forma muy básica, nos vamos a centrar más en el aspecto de su comportamiento de renderización y que tal eficiente es.
Veremos como se comporta la aplicación en lo que respecta a la renderización y mediante el uso de cambios que se realiza desde el componente Padre.
Cogemos el código anterior y hacemos algunas modificaciones, añadiendo con un elemento que almacenará el estado de una generación de un número aleatorio que obtenemos por cada click.
Aparte de eso, cambiamos el nombre del componente a <RandomPanel>
y dentro de el sustituimos las etiquetas button
por div
// 1: Importamos el elemento Slot para proyecciones
import { Slot, component$, useSignal} from "@builder.io/qwik";
// 2 Componente (hijo) donde proyectaremos lo que pasamos desde el padre a "Slot"
const RandomPanel = component$(() => {
console.log("Renderizado (Random Panel")
return (
<div>
Lo que se proyecta: <Slot />
</div>
);
});
// 3 Componente principal que usa "RandomPanel" y pasa "Contenido..." para proyectarse dentro de "Slot"
export default component$(() => {
const minValue = 0;
const maxValue = 20;
const randomValue = useSignal(0);
console.log("Renderizado (default)");
return (
<>
<RandomPanel>
Número: ({randomValue.value}) dentro del componente {'<Button>'} marcado con el elemento {`<Slot>`}
</RandomPanel>
<button onClick$={() => randomValue.value = Math.floor(Math.random() * (maxValue - minValue + 1) + minValue)}>
Nuevo número
</button>
</>
);
});
Al guardar los cambios, renderiza una vez el principal (padre) y el <RandomPanel>
en el lado del servidor:
Ahora cuando se de cualquier cambio de estado en randomValue
, se mostrará el cambio que se ha dado dentro del registro de la consola de nuestro navegador, es decir, en el lado cliente.
Por el momento esto es lo que se muestra, que son los mensajes que se visualizan por defecto:
Estará a la espera de cualquier cambio y para que se de un cambio de estado, vamos a realizar nuestro primer click al botón para obtener un nuevo número aleatorio con Nuevo número
:
Y si hacemos 3 clicks más (en total 4):
Como se puede observar el funcionamiento sigue siendo el mismo, se comporta igual, renderiza los componentes por primera vez desde el servidor y cualquier cambio que se da en estos momentos lo gestiona desde el lado del cliente.
Named Slots (Ranuras nombradas)
En el primer caso, el más simple, todo el contenido se proyecta en un <Slot>
, haciendo que el contenido del componente padre se proyecte en el componente hijo completamente en la ranura existente (la única).
En casos más complejos, puede haber más de una ranura de contenido que deba proyectarse necesitando tener múltiples ranuras de contenido, donde habrá que asignarles nombres a cada una de ellas.
Para añadir una referencia de nombre a una ranura, tenemos que partir de la base en la que teníamos un elemento <Slot>
(sin nada más de información) y para especificar la ranura con su nombre debemos de añadirle la propiedad q:slot
y junto con ella el valor del nombre de esa ranura.
Imaginaros que queremos proyectar título (title
) y descripción (description
) de un elemento cualquiera.
Con un <Slot>
simple lo implementamos así:
// 1: Importamos el elemento Slot para proyecciones
import { Slot, component$ } from '@builder.io/qwik';
// 2 Componente donde proyectaremos lo que pasamos desde el padre a "Slot"
const Element= component$(() => {
return (
<Slot />
);
});
// 3 Componente principal que usa "Button" y pasa "Contenido..." para proyectarse dentro de "Slot"
export default component$(() => {
return (
<Element>
<h1>Elemento título</h1>
<p>Elemento descripción</p>
</Element>
);
});
Y el resultado será algo como esto:
Y si queremos poder proyectarlo haciendo uso de esta nueva opción con las proyecciones mediante referencias nombradas, debemos de implementarlo así, basándonos en lo anterior y especificando correctamente el nombre de la ranura a los elemento seleccionados:
// 1: Importamos el elemento Slot para proyecciones
import { Slot, component$ } from '@builder.io/qwik';
// 2 Componente donde proyectaremos lo que pasamos desde el padre a los "Slot" que está referenciados por nombre
const Element= component$(() => {
return (
<>
<h1><Slot name="title"/></h1>
<p><Slot name="description" /></p>
</>
);
});
// 3 Componente principal que usa "Element" y pasa el contenido asignando a las ranuras
export default component$(() => {
return (
<Element>
<span q:slot="title">Elemento título</span>
<span q:slot="description">Elemento Descripción</span>
</Element>
);
});
El resultado prácticamente es el mismo:
Y si añado en el componente padre un nuevo elemento sin usar la propiedad ni para especificar el título (title
) ni la descripción (description
), esa información no debería de proyectarse. Añadimos este nuevo elemento con el contenido Elemento sin referencia q:slot
:
// 1: Importamos el elemento Slot para proyecciones
import { Slot, component$ } from '@builder.io/qwik';
// 2 Componente donde proyectaremos lo que pasamos desde el padre a los "Slot" que está referenciados por nombre
const Element= component$(() => {
return (
...
);
});
// 3 Componente principal que usa "Element" y pasa el contenido asignando a las ranuras
export default component$(() => {
return (
<Element>
<span q:slot="title">Elemento título</span>
<span q:slot="description">Elemento Descripción</span>
// Añadimos el nuevo elemento sin especificar la referencia
<span>Elemento sin referencia q:slot</span> <=======
</Element>
);
});
Y si guardamos los cambios, como he mencionado, no tiene en cuenta el texto Elemento sin referencia q:slot
, quedándose prácticamente igual que sin haberlo añadido porque no sabe donde hay que proyectarlo.
¿Qué pasaría si ahora añadimos q:slot=title a ese elemento?
Si añadimos ese valor al elemento mencionado, lo que hará es proyectar esa información junto con la primera, la que contiene Elemento título
.
Añadimos ese cambio al código y guardamos:
// 1: Importamos el elemento Slot para proyecciones
import { Slot, component$ } from '@builder.io/qwik';
// 2 Componente donde proyectaremos lo que pasamos desde el padre a los "Slot" que está referenciados por nombre
const Element= component$(() => {
...
});
// 3 Componente principal que usa "Element" y pasa el contenido asignando a las ranuras
export default component$(() => {
return (
<Element>
<span q:slot="title">Elemento título</span>
<span q:slot="description">Elemento Descripción</span>
<span q:slot="title">Elemento sin referencia q:slot</span> <======
</Element>
);
});
Como se puede observar, se proyectan los dos a la vez, no reescribe el segundo dejando de mostrar el contenido del primero:
Esto sería lo fundamental para entender bien el uso de los Named Slots
(Ranuras nombradas).
También tenemos opción de trabajar con ellas, renderizándolas dependiendo de unas condiciones que se modifican dependiendo del cambio de estado.
Named Slots condicional
Imaginaros que tenemos un ejemplo en el que hemos creado el componente Collapsable
que alterna entre los estados abierto y cerrado.
Actualmente, cuando Collapsable
esté cerrado (teniendo el valor de un estado como referencia), muestra el contenido implementado dentro de él.
Tendremos dos tipos de ranuras:
open
: Para mostrar el contenido que se visualiza cuando elCollapsable
está en un estado abiertoclosed
: Mostrará el contenido que hace referencia a que elCollapsable
está cerrado
No cambia mucho respecto a lo anterior, lo único que tenemos que tener en cuenta es que se mostrará uno u otro dependiendo del valor del estado.
Para implementar lo siguiente, lo primero necesitamos el componente hijo, el que mostrará un elemento Collapsable
con dos ranuras, la llamada open
y la closed
: que se visualizarán dependiendo del valor almacenado en la propiedad open
dentro de store
import { component$, useStore } from '@builder.io/qwik';
export const Collapsable = component$(() => {
console.log('Render: <Collapsable>');
// 1.- Controlamos el estado de si está abierto o no
const store = useStore({ open: true });
// 2.- Se muestra el contenido de una ranura u otra dependiendo de "open"
return (
<div onClick$={() => (store.open = !store.open)}>
{store.open ? <Slot name="open" /> : <Slot name="closed" />}
</div>
);
});
Ahora que está definido el componente hijo, con las ranuras especificadas, tenemos que crear el componente padre para proyectar la información en los dos casos, siendo este todo el código necesario:
import { component$, Slot, useStore } from '@builder.io/qwik';
export default component$(() => {
console.log('Render: <App>');
return (
<Collapsable>
<div q:slot="closed">▶ Haz click para ver su contenido</div>
<div q:slot="open">
▼<div> Contenido del collapsable visible por estar en estado abierto</div>
</div>
</Collapsable>
);
});
export const Collapsable = component$(() => {
console.log('Render: <Collapsable>');
const store = useStore({ open: true });
return (
<div onClick$={() => (store.open = !store.open)}>
{store.open ? <Slot name="open" /> : <Slot name="closed" />}
</div>
);
});
Guardamos y visualizamos el resultado, que viendo lo especificado nos tiene que mostrar lo proyectado en open
, por ser el valor de la propiedad open
en el estado true
:
Si hacemos click en el contenido que se ve, se cambia de estado (open = false
) y se muestra el estado cerrado haciendo referencia a q:slot="closed"
Podéis hacer la comprobación de lo que es la renderización para este caso particular, como en el primer ejemplo donde debe generar tanto el contenido predeterminado como el contenido cerrado que Qwik debe serializar.
La ventaja es que cuando Collapsable
alterna entre los estados abierto y cerrado, no necesita volver a renderizar el componente padre (default
) para recuperar el contenido que se proyectó en él.
Una vez visto el apartado de los named slots
, pasamos el apartado de los contenidos de respaldo para mostrar contenido en casos que no se proyecten bien los datos deseados.
Contenido de respaldo (Fallback content)
El contenido de respaldo nos va a permitir que el componente hijo muestre contenido alternativo en caso de que el componente padre no proporcione contenido correspondiente.
El contenido de respaldo se puede realizar con CSS y es necesario hacer uso de las pseudo clases de CSS (y el CSS correspondiente) en la ranura nombrada en caso de que esté vacía.
Una vez que sabemos esto, empezamos a trabajar con el siguiente ejemplo, donde mostraremos cuatro tarjetas, en diferentes variantes, con todo el contenido correctamente añadido y en algunas de ellas sin estar llenas de contenido.
Para los casos que no tenga el contenido correctamente añadido, usaremos las pseudo clases de CSS adecuadas para especificar el contenido de respaldo.
Empezamos con el contenido donde si se añade correctamente todo el contenido.
Creamos el componente Card
, con las diferentes ranuras, que serán title
y body
:
import { component$, Slot} from '@builder.io/qwik';
export const Card = component$(() => {
return (
<article class="card">
<header class="title">
<Slot name="title"></Slot>
</header>
<section class="body">
<Slot name="body"></Slot>
</section>
</article>
);
});
Añadimos los siguientes estilos para la tarjetas:
.card {
border-radius: 5px;
vertical-align: top;
display: inline-block;
border: 1px solid grey;
width: 200px;
margin: .5em;
}
.title {
background-color: lightgray;
padding: 0.5em;
border-bottom: 1px solid black;
}
.body {
padding: 0.5em;
}
Implementándolo junto con el código del componente Card
:
import { component$, Slot, useStyles$ } from '@builder.io/qwik';
export const Card = component$(() => {
useStyles$(CSS);
return (
<article class="card">
<header class="title">
<Slot name="title"></Slot>
</header>
<section class="body">
<Slot name="body"></Slot>
</section>
</article>
);
});
export const CSS = `
.card {
border-radius: 5px;
vertical-align: top;
display: inline-block;
border: 1px solid grey;
width: 200px;
margin: .5em;
}
.title {
background-color: lightgray;
padding: 0.5em;
border-bottom: 1px solid black;
}
.body {
padding: 0.5em;
}
`;
Ahora lo que nos quedará es proyectar la información de un elemento en ese componente Card
, teniendo en cuenta las ranuras que hemos especificado para mostrar el título (title
) y el contenido del body
:
import { component$, Slot, useStyles$ } from '@builder.io/qwik';
export const Card = component$(() => {
useStyles$(CSS);
return (
...
});
export default component$(() => {
return (
<>
<Card>
<span q:slot="title">Qwik</span>
<span q:slot="body">Qwik es un framework reanudable para construir aplicaciones web instantáneas.</span>
</Card>
</>
);
});
export const CSS = `
...
`;
Guardamos los cambios y vemos los resultados:
Podemos añadir nuevos elementos dentro del componente padre como nuevo Card
y proyectando la información deseada:
export default component$(() => {
return (
<>
<Card>
<span q:slot="title">Qwik</span>
<span q:slot="body">Qwik es un framework reanudable para construir aplicaciones web instantáneas.</span>
</Card>
<Card>
<span q:slot="title">Qwik - Curso en Español</span>
<span q:slot="body">
Curso paso a paso para aprender Qwik gracias a Anartz Mugika Ledo. Más información:
<br/>
<a href="https://medium.com/@mugan86/list/qwik-paso-a-paso-desde-0-al-detalle-e7df8b471166" target='_blank'>Curso Qwik</a>
</span>
</Card>
</>
);
});
Cuyo resultado podemos observar
Por el momento no tenemos problemas en el aspecto visual por estar metiendo toda la información necesaria.
Tenemos que ser un poco desconfiados y hay que analizar los casos en los que pueda darse la situación en la que no metamos toda la información, sea el título (title
) o sea el contenido del body
(o los dos sin introducir nada)
Probamos añadiendo dos componentes más, en los que tendremos este contenido, ignorando el body
en el primer caso y title
en el segundo:
<Card>
<span q:slot="title">Partytown</span>
</Card>
<Card>
<span q:slot="body">
Builder.io te permite construir visualmente en tu stack tecnológico. Capacita a todo tu equipo para crear y optimizar experiencias de alta velocidad en tus sitios y aplicaciones. Brinda autonomía a todo el equipo con una plataforma aprobada por los desarrolladores.
</span>
</Card>
Aplicándolo en nuestro proyecto actual:
export default component$(() => {
return (
<>
<Card>
<span q:slot="title">Qwik</span>
<span q:slot="body">Qwik es un framework reanudable para construir aplicaciones web instantáneas.</span>
</Card>
<Card>
<span q:slot="body">
Curso paso a paso para aprender Qwik gracias a Anartz Mugika Ledo. Más información:
<br/>
<a href="https://medium.com/@mugan86/list/qwik-paso-a-paso-desde-0-al-detalle-e7df8b471166" target='_blank'>Curso Qwik</a>
</span>
<Card>
<Card>
<span q:slot="title">Partytown</span>
</Card>
<Card>
<span q:slot="body">
Builder.io te permite construir visualmente en tu stack tecnológico. Capacita a todo tu equipo para crear y optimizar experiencias de alta velocidad en tus sitios y aplicaciones. Brinda autonomía a todo el equipo con una plataforma aprobada por los desarrolladores.
</span>
</Card>
</>
);
});
Cuyo resultado se refleja de la siguiente forma:
Y como se puede apreciar, al no haber especificado el contenido completamente en los dos últimos elementos, donde el primero no tiene el body
y el segundo el title
, se muestra vacío de esa manera y tenemos que hacer que en casos así, se de feedback al usuario para advertir de que no se visualiza bien por un motivo u otro. ¿Cómo podemos hacerlo? Se puede hacer con CSS mismo.
Añadiendo mensaje feedback en los apartados vacíos
Para poder añadir contenido mediante CSS en los casos que un elemento esté vacío, necesitamos combinar las pseudo clases :empty y ::before cuya documentación de como funcionan al detalle os lo proporciono a continuación:
Teniendo eso como referencia, vamos a implementar el feedback que corresponde al elemento de título (title
) donde mostraremos un mensaje sencillo y claro, para que la persona que esté usando la aplicación tenga claro que hay un problema, y ahí es donde ponemos en color rojo este contenido:
Debes añadir un título con q:slot="title"
Para hacerlo con CSS, añadimos este código al CSS existente:
/* Hacemos rerefencia a la clase "title" */
.title:empty::before {
content: 'Debes de añadir un título con q:slot="title"';
color: red;
}
Quedando el CSS de la siguiente forma (sin cambios en el componente):
.card {
border-radius: 5px;
vertical-align: top;
display: inline-block;
border: 1px solid grey;
width: 200px;
margin: .5em;
}
.title {
background-color: lightgray;
padding: 0.5em;
border-bottom: 1px solid black;
}
.body {
padding: 0.5em;
}
/* Hacemos rerefencia a la clase "title" */
.title:empty::before {
content: 'Debes de añadir un título con q:slot="title"';
color: red;
}
Y una vez guardado, recargaremos la página y podemos apreciar un cambio, que en la última tarjeta que no contenía información proyectada al slot title
, tiene contenido con la advertencia que hemos añadido por CSS:
Perfecto, ¿Ahora seríais capaces de hacer lo mismo con la clase body
que corresponde al slot body
?
Sin seguir adelante, os animo a que lo probéis, no es complicado. El resultado debe de ser el siguiente, añadiendo un texto con el mensaje Debes añadir un contenido con q:slot="body"
y su color que sea naranja (orange)
Yo os proporciono la solución a continuación para conseguirlo.
La solución es la siguiente (espero que lo hayáis intentado), aplicando el siguiente CSS:
.body:empty::before {
content: 'Debes de añadir un contenido con q:slot="body"';
color: orange;
}
Quedando todo el CSS de la siguiente forma:
.card {
border-radius: 5px;
vertical-align: top;
display: inline-block;
border: 1px solid grey;
width: 200px;
margin: .5em;
}
.title {
background-color: lightgray;
padding: 0.5em;
border-bo ttom: 1px solid black;
}
.body {
padding: 0.5em;
}
/* Hacemos rerefencia a la clase "title" */
.title:empty::before {
content: 'Debes de añadir un título con q:slot="title"';
color: red;
}
.body:empty::before {
content: 'Debes de añadir un contenido con q:slot="body"';
color: orange;
}
Y llegados a este punto, hemos completado todos los objetivos de los puntos a tratar y vamos a finalizar el artículo.
Conclusión
La Content Projection
son útiles para crear componentes más flexibles y reutilizables, ya que nos permiten personalizar el contenido de un componente sin necesidad de modificar su lógica interna. Además, nos permiten componer jerarquías de componentes de manera más intuitiva y modular.
En resumen, los Content Projection en Qwik (y otras tecnologías como Angular, React,…) es una forma de pasar y mostrar contenido dinámico en un componente reutilizable, permitiendo una mayor flexibilidad y modularidad en la estructura de tus aplicaciones.
Os dejo el repositorio con todo lo visto en el artículo:
Todos los artículos publicados del curso los encontraréis en la siguiente lista que iré actualizando semanalmente y estableciendo el orden natural recomendado:
Presencia en redes sociales
Podéis encontrarme en las siguientes redes.