Ir al contenido
wannacode

Condiciones: vacío, error, cargando

En resumen: El renderizado condicional muestra solo la parte del marcado que corresponde a los datos (cargando, error, vacío o la lista); en Vue con v-if, v-else y v-show.

Problema

Cargando, error y la lista a la vez

El catálogo se ha cargado. Pero bajo el encabezado dice «Cargando…», luego «Los productos no se cargaron.», luego «No hay productos en esta categoría.», y solo después vienen los productos.

Vue

El componente Catalog recibe status y los productos como props. Pero no los mira: siempre dibuja las cuatro partes. El comprador no sabe qué creer, y un lector de pantalla lee tanto el error como los productos.

La idea

La parte que corresponde a los datos

El catálogo tiene cuatro estados: cargando, error, vacío y listo. En la pantalla debe estar solo la parte del estado actual. Esto es el renderizado condicional: el componente decide qué devolver según los datos.

En Vue las condiciones son atributos en la plantilla:

<p v-if="status === 'loading'" role="status">Cargando…</p>
<p v-else-if="status === 'error'" role="alert">Los productos no se cargaron.</p>
<p v-else-if="status === 'empty'">No hay productos en esta categoría.</p>
<ul v-else>…</ul>

v-if dibuja el elemento solo si la expresión es verdadera. v-else-if y v-else continúan la cadena y deben ir justo después del elemento anterior de ella. Se dibuja exactamente un elemento de la cadena.

Las dos bibliotecas resuelven lo mismo a su manera: React con JavaScript alrededor del marcado, Vue con atributos dentro. El resultado es el mismo: el elemento de una condición falsa no está en el DOM. No está oculto, no existe.

Aquí status se pasa como prop. Cómo lo cambia el propio catálogo mientras carga desde la API está en el módulo «Efectos, datos y formularios».

Míralo

Ver qué rama entra

Un valor entra en el componente, y solo una rama entra en el DOM. Fíjate en la columna «DOM» en cada cambio de status.

<template>
<p v-if="status === 'loading'">Cargando…</p>
<p v-else-if="status === 'error'">Error</p>
<p v-else-if="status === 'empty'">No hay productos</p>
<ul v-else>…</ul>
</template>
<!-- en el DOM, pero oculto -->
<p v-show="status === 'loading'">Cargando…</p>
Paso a paso, en palabras
  1. status llega al componente como prop. Es 'loading': v-if coincide, y se dibuja solo <p>Cargando…</p>. Las otras ramas no están en el DOM.
  2. status pasa a 'error'. La biblioteca dibuja otra vez: ahora coincide la segunda rama. <p>Cargando…</p> sale del DOM, y entra el mensaje de error.
  3. 'empty': solo el mensaje de que la categoría está vacía.
  4. 'ready': ni v-if ni v-else-if coinciden, así que se dibuja v-else: la lista. En el DOM hay siempre una rama.
  5. La otra manera: v-show dibuja siempre el elemento y solo cambia su display. Con loading, se ve.
  6. status pasa a 'ready'. El elemento se queda en el DOM, con todo lo que tiene dentro, y solo recibe display: none. Nadie lo ve, y un lector de pantalla lo salta.
  7. v-if quita el elemento del DOM, v-show solo lo oculta. v-if es lo habitual; v-show es para algo que se cambia a menudo o que debe conservar lo escrito.

La primera parte es el renderizado condicional: con un valor nuevo, el elemento viejo sale del DOM y entra el nuevo. La segunda parte es ocultar: el elemento está en el DOM todo el tiempo y solo se muestra o se oculta. De eso trata el final de la lección.

Pruébalo

Los cuatro estados

Catalog dibuja todo a la vez. Haz que cada sección tenga solo la parte de su estado: «Cargando…» para cargando, el error para error, «No hay productos en esta categoría.» para vacío y la lista para listo. Las otras partes no deben estar en la página.

Vue

Pruébalo

Completa el hueco

«Cargando…» debe aparecer solo mientras status es loading.

<p ="status === 'loading'" role="status">Cargando…</p>

La idea

Agotado: una insignia y un botón desactivado

Una condición no es solo para partes enteras. En la tarjeta del producto cambia un texto y un atributo:

<p v-if="product.stock === 0" class="badge">Agotado</p>
<p v-else>Quedan {{ product.stock }} uds.</p>
<button type="button" :disabled="product.stock === 0">Añadir</button>

:disabled="true" pone el atributo disabled, y :disabled="false" lo quita.

Un botón desactivado no se puede pulsar, no recibe el foco con Tab, y un lector de pantalla dice que no está disponible. La insignia dice por qué. Un botón que se puede pulsar y luego dice «sin existencias» es peor: el comprador se entera solo después del clic.

Pruébalo

Agotado

Haz que ProductCard muestre «Agotado» y desactive el botón «Añadir» cuando stock sea 0. Si no, muestra «Quedan 5 uds.» y el botón funciona. El queso está agotado.

Vue

La idea

El cero en la pantalla

v-if mira si el valor es verdadero, así que v-if="items.length" con un carrito vacío no dibuja nada. El cero aparece cuando la condición está en {{ }}:

<p>{{ items.length && `En el carrito: ${items.length}` }}</p> <!-- carrito vacío: 0 -->
<p v-if="items.length > 0">En el carrito: {{ items.length }}</p> <!-- carrito vacío: nada -->

&& no devuelve true o false. Devuelve el lado izquierdo si es falsy: aquí 0, y {{ }} lo muestra. {{ false }} incluso muestra la palabra «false». Escribe las condiciones con v-if, y deja {{ }} para los valores.

Pruébalo

Encuentra el error

Con el carrito vacío, en la esquina de arriba pone «0».

Elige la línea con el error.

La idea

Quitado u oculto

A veces un elemento tiene que quedarse en el DOM y solo no verse. Aquí los dos paneles tienen un campo para una nota. Abre los dos, escribe algo, ciérralos y ábrelos otra vez.

Vue
En la primera sección el panel usa v-if: cerrado desaparece, y abierto es nuevo, con el campo vacío. En la segunda usa v-show: se queda en el DOM con display: none y conserva lo escrito.

Quitar es lo habitual: menos elementos en la página, nada oculto para un lector de pantalla. Ocultar es para partes que se abren y se cierran a menudo o tienen que conservar lo que se introdujo en ellas.

Más precisamente, para curiosos

v-if en <template>. Para mostrar varios elementos bajo una condición sin envoltorio, pon v-if en un <template>: no entra en el DOM. v-show no funciona en <template>, porque no hay un elemento al que poner display: none.

El coste. v-if hace y quita los elementos en cada cambio, mientras que v-show los hace una vez, en el primer dibujado, y después solo cambia display. Así que v-show es para partes que cambian a menudo, y v-if para partes que se muestran pocas veces.

v-if y v-for. En Vue 3 un v-if en el mismo elemento que v-for se ejecuta primero y no ve la variable del bucle. Pon el v-if en un envoltorio o filtra los datos.

La Options API del código antiguo usa los mismos v-if, v-else y v-show.

aria-hidden no es ocultar. aria-hidden="true" oculta el elemento solo de un lector de pantalla, y se queda en la pantalla. Ocultar para todos es display: none: hidden, v-show o quitar.

Ejercicio

El carrito

App muestra tres carritos: vacío, con productos y con un producto agotado. Haz Cart de modo que el vacío diga «El carrito está vacío.», sin lista y sin total, y el botón «Hacer el pedido» esté desactivado. El lleno muestra las filas, «Total: 4,00 €» y un botón que funciona. El producto agotado tiene una insignia «Agotado», encima del botón dice «Quita los productos agotados para hacer el pedido.» (rol alert), y el botón está desactivado.

Vue

Test

Ponte a prueba

1. La condición de «Cargando…» es falsa. ¿Qué hay en su lugar en el DOM?
2. El carrito está vacío. ¿Qué muestra <p>{{ items.length && 'Hay productos' }}</p>?
3. ¿Qué es cierto sobre v-else?
4. Un panel con un campo de nota se abre y se cierra a menudo, y lo escrito tiene que quedarse. ¿Qué encaja mejor?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·