Skip to content

Unidad 12: Renderizado Condicional en Vue.js

Vue.js ofrece herramientas poderosas para controlar la visualización de elementos en tu interfaz de usuario basándose en condiciones específicas. Esto se logra mediante las directivas v-if, v-else-if, v-else y v-show.

12.1 v-if: Renderizado Condicional Real

La directiva v-if permite incluir o excluir elementos del DOM en función de una expresión booleana.

Uso básico:

Ejemplo de v-if en Vue.js

<template>
<div>
    <p v-if="isVisible">Este párrafo es visible.</p>
    <button @click="toggleVisibility">Alternar Visibilidad</button>
</div>
</template>

<script>
export default {
data() {
    return {
    isVisible: true
    };
},
methods: {
    toggleVisibility() {
    this.isVisible = !this.isVisible;
    }
}
};
</script>

En este ejemplo, el párrafo solo se renderiza si isVisible es true. Al hacer clic en el botón, se alterna su visibilidad.

Consideraciones:

  • v-if elimina y recrea el elemento en el DOM según la condición, lo que puede ser más costoso en términos de rendimiento si la condición cambia con frecuencia.
  • Es recomendable usar v-if cuando la condición no cambia con frecuencia.

12.2 v-else y v-else-if: Manejo de Alternativas

Estas directivas complementan a v-if, permitiendo manejar múltiples condiciones de manera secuencial.

Uso de v-else-if y v-else:

Ejemplo de v-else-if y v-else en Vue.js

<template>
<div>
    <p v-if="status === 'loading'">Cargando...</p>
    <p v-else-if="status === 'error'">Error al cargar datos.</p>
    <p v-else>Contenido cargado exitosamente.</p>
</div>
</template>

<script>
export default {
data() {
    return {
    status: 'loading' // Puede ser 'loading', 'error' o 'loaded'
    };
}
};
</script>

En este caso, se evalúan múltiples estados y se renderiza el bloque correspondiente según el valor de status.

Nota

  • v-else-if se utiliza para agregar condiciones adicionales después de un v-if.
  • v-else se utiliza para manejar el caso en que ninguna de las condiciones anteriores se cumple.

12.3 v-show: Alternativa para Mostrar/Ocultar Elementos

La directiva v-show también permite controlar la visibilidad de un elemento, pero a diferencia de v-if, el elemento siempre se renderiza en el DOM y solo se oculta o muestra cambiando su propiedad CSS display.

Uso básico:

Ejemplo de v-show en Vue.js

<template>
<div>
    <p v-show="isVisible">Este párrafo es visible.</p>
    <button @click="toggleVisibility">Alternar Visibilidad</button>
</div>
</template>

<script>
export default {
data() {
    return {
    isVisible: true
    };
},
methods: {
    toggleVisibility() {
    this.isVisible = !this.isVisible;
    }
}
};
</script>

Consideraciones:

  • v-show es más eficiente si la visibilidad del elemento cambia con frecuencia, ya que no implica la destrucción y recreación del DOM.
  • Sin embargo, el elemento siempre estará presente en el DOM, lo que puede no ser deseable si se trata de contenido sensible o pesado.

12.4 Uso de v-if con <template>: Renderizado de Múltiples Elementos

Si necesitas aplicar v-if a múltiples elementos adyacentes, puedes utilizar la etiqueta <template> como contenedor sin que se refleje en el DOM final.

Ejemplo:

<template v-if="isVisible">
  <h1>Título Visible</h1>
  <p>Este contenido es visible.</p>
</template>

En este caso, <template> actúa como un fragmento que agrupa múltiples elementos sin agregar un nodo extra al DOM.

Nota

v-if no es compatible con <template> en Vue.js 2.x, pero esta limitación se ha eliminado en Vue.js 3.x.

12.5 Comparativa entre v-if y v-show

Característica v-if v-show
Renderizado Inicial No renderiza el elemento si la condición es falsa. Renderiza el elemento y lo oculta con CSS si la condición es falsa.
Eficiencia Más eficiente si la condición rara vez cambia. Más eficiente si la condición cambia con frecuencia.
Visibilidad Añade o elimina el elemento del DOM. Alterna la visibilidad mediante CSS.
Uso Recomendado Condiciones que rara vez cambian. Condiciones que cambian con frecuencia.
  • v-if: Realiza un renderizado condicional completo, añadiendo o eliminando el elemento del DOM según la condición. Es más adecuado cuando la condición no cambia frecuentemente.
  • v-show: Solo alterna la visibilidad del elemento mediante CSS, manteniéndolo siempre en el DOM. Es más eficiente cuando la visibilidad del elemento cambia con frecuencia.

Resumen

  • Usa v-if cuando la condición rara vez cambia y deseas evitar la renderización inicial del elemento.
  • Usa v-show cuando la condición cambia con frecuencia y necesitas un cambio de visibilidad más eficiente.

12.6 Aplicación a nuestro ejemplo ToDo

Ahora es el momento de añadir una de las funciones principales que aún nos falta: la posibilidad de editar elementos de la lista de tareas. Para ello, aprovecharemos las funciones de renderizado condicional de Vue ( v-if y v-else) para alternar entre la vista de elementos de la lista y una vista de edición donde se pueden actualizar las etiquetas. También veremos cómo añadir la función para eliminar elementos de la lista.

12.6.1 Creación de un componente para la edición de tareas

Podemos empezar creando un componente independiente para gestionar la función de edición. En tu directorio components, crea un archivo llamado ToDoItemEditForm.vue. Copia el siguiente código en ese archivo:

código de ToDoItemEditForm.vue

<template>
    <form class="stack-small" @submit.prevent="onSubmit">
        <div>
        <label class="edit-label">Edit Name for &quot;{{label}}&quot;</label>
        <input
            :id="id"
            type="text"
            autocomplete="off"
            v-model.lazy.trim="newLabel" />
        </div>
        <div class="btn-group">
        <button type="button" class="btn" @click="onCancel">
            Cancel
            <span class="visually-hidden">editing {{label}}</span>
        </button>
        <button type="submit" class="btn btn__primary">
            Save
            <span class="visually-hidden">edit for {{label}}</span>
        </button>
        </div>
    </form>
</template>
<script>
export default {
    props: {
    label: {
        type: String,
        required: true,
    },
    id: {
        type: String,
        required: true,
    },
    },
    data() {
    return {
        newLabel: this.label,
    };
    },
    methods: {
    onSubmit() {
        if (this.newLabel && this.newLabel !== this.label) {
        this.$emit("item-edited", this.newLabel);
        }
    },
    onCancel() {
        this.$emit("edit-cancelled");
    },
    },
};
</script>
<style scoped>
.edit-label {
    font-family: Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    color: #0b0c0c;
    display: block;
    margin-bottom: 5px;
}
input {
    display: inline-block;
    margin-top: 0.4rem;
    width: 100%;
    min-height: 4.4rem;
    padding: 0.4rem 0.8rem;
    border: 2px solid #565656;
}
form {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
}
form > * {
    flex: 0 0 100%;
}
</style>

Revisar

Nota Revise el código anterior y luego lea la descripción a continuación para asegurarse de comprender todo lo que hace el componente antes de continuar. Esta es una forma útil de reforzar todo lo aprendido hasta ahora.

Este código configura la función de edición. Creamos un formulario con un campo <input> para editar el nombre de nuestra tarea.

Hay un botón "Guardar" y un botón "Cancelar":

  • Cuando se hace clic en el botón "Guardar", el componente emite la nueva etiqueta a través de un evento item-edited.
  • Cuando se hace clic en el botón "Cancelar", el componente lo señala emitiendo un evento edit-cancelled.

12.6.2 Añadir la función de edición a la lista de tareas

Antes de añadir elementos ToDoItemEditForm a nuestra aplicación, necesitamos realizar algunas modificaciones en nuestro componente ToDoItem. En concreto, necesitamos añadir una variable para saber si el elemento se está editando y un botón para activar o desactivar dicha variable. También añadiremos un botón Delete, ya que la eliminación está estrechamente relacionada.

Actualice su plantilla ToDoItem como se muestra a continuación:

ToDoItem

<template>
<div class="stack-small">
    <div class="custom-checkbox">
    <input
        type="checkbox"
        class="checkbox"
        :id="id"
        :checked="isDone"
        @change="$emit('checkbox-changed')" />
    <label :for="id" class="checkbox-label">{{label}}</label>
    </div>
    <div class="btn-group">
    <button type="button" class="btn" @click="toggleToItemEditForm">
        Edit <span class="visually-hidden">{{label}}</span>
    </button>
    <button type="button" class="btn btn__danger" @click="deleteToDo">
        Delete <span class="visually-hidden">{{label}}</span>
    </button>
    </div>
</div>
</template>

Hemos agregado un envoltorio alrededor <div> de toda la plantilla para fines de diseño.

También hemos agregado los botones "Editar" y "Eliminar":

  • Al hacer clic en el botón "Editar", se activará la visualización del componente ToDoItemEditForm para que podamos usarlo para editar nuestra tarea, mediante una función de controlador de eventos llamada toggleToItemEditForm(). Este controlador establecerá un indicador isEditing como verdadero. Para ello, primero debemos definirlo dentro de nuestra propiedad data().
  • Al hacer clic en el botón "Eliminar", se eliminará la tarea pendiente mediante una función de controlador de eventos llamada deleteToDo(). En este controlador, emitiremos un evento item-deleted a nuestro componente principal para que la lista se actualice.

Definamos nuestros controladores de clic y la bandera isEditing necesaria.

Agregue isEditing a continuación su punto de datos existente isDone:

1
2
3
4
5
6
data() {
  return {
    isDone: this.done,
    isEditing: false
  };
}

Ahora agregue sus métodos dentro de una propiedad de métodos, justo debajo de su data()propiedad:

1
2
3
4
5
6
7
8
methods: {
    deleteToDo() {
      this.$emit('item-deleted');
    },
    toggleToItemEditForm() {
      this.isEditing = true;
    }
  }

12.6.3 Añadir renderizado condicional a la lista de tareas

Ahora tenemos una bandera isEditing que podemos usar para indicar si el elemento se está editando (o no). Si isEditing es verdadero, queremos usar esa bandera para mostrar "our" ToDoItemEditForm en lugar de la casilla de verificación. Para ello, usaremos otra directiva de Vue: v-if.

La directiva v-if solo renderizará un bloque si el valor que se le pasa es verdadero. Esto es similar al funcionamiento de una sentencia if en JavaScript. v-if también cuenta con directivas v-else-ify correspondientes v-else para proporcionar el equivalente de JavaScript else ifelsela lógica dentro de las plantillas de Vue.

Es importante tener en cuenta que los bloques v-elsev-else-if deben ser el primer hermano de un bloque v-if v-else-if; de lo contrario, Vue no los reconocerá. También puedes adjuntarlos v-if a una etiqueta <template> si necesitas renderizar condicionalmente una plantilla completa.

Por último, puedes usar un  v-ifv-else en la raíz de tu componente para mostrar solo uno de los bloques, ya que Vue solo renderiza uno a la vez. Lo haremos en nuestra aplicación, ya que nos permitirá reemplazar el código que muestra nuestra tarea pendiente con el formulario de edición.

En primer lugar, agregue v-if="!isEditing" a la raíz <div>de su componente ToDoItem:

<div class="stack-small" v-if="!isEditing"></div>

A continuación, debajo de esa etiqueta <div> de cierre, agregue la siguiente línea:

<to-do-item-edit-form v-else :id="id" :label="label"></to-do-item-edit-form>

También necesitamos importar y registrar el componenete ToDoItemEditForm para poder usarlo dentro de esta plantilla. Agrega esta línea al principio del elemento <script>:

import ToDoItemEditForm from "./ToDoItemEditForm";

Y agregue una propiedad components encima de la propiedad props dentro del objeto componente:

1
2
3
components: {
  ToDoItemEditForm
},

Ahora, si vas a tu aplicación y haces clic en el botón "Editar" de un elemento de la tarea pendiente, deberías ver la casilla de verificación reemplazada con el formulario de edición.

ToDoItemEditForm

Sin embargo, actualmente no hay forma de volver atrás. Para solucionarlo, necesitamos agregar más controladores de eventos a nuestro componente.

12.6.4 Añadir controladores de eventos para la edición de tareas

Primero, necesitamos agregar un método itemEdited() a la propiedad methods de nuestro componente ToDoItem. Este método debe tomar la etiqueta del nuevo elemento como argumento, emitir un evento itemEdited al componente principal y establecer isEditingfalse.

Agreguelo ahora, debajo de sus métodos existentes:

1
2
3
4
itemEdited(newLabel) {
  this.$emit('item-edited', newLabel);
  this.isEditing = false;
}

A continuación, necesitaremos un método editCancelled(). Este método no acepta argumentos y solo sirve para establecer valor de retorno de isEditingfalse. Agregue este método debajo del anterior:

1
2
3
editCancelled() {
  this.isEditing = false;
}

Por último, en esta sección, agregaremos controladores de eventos para los eventos emitidos por el componente ToDoItemEditForm y adjuntaremos los métodos apropiados a cada evento.

Actualice su elemento <to-do-item-edit-form></to-do-item-edit-form> para que se vea así:

1
2
3
4
5
6
7
<to-do-item-edit-form
  v-else
  :id="id"
  :label="label"
  @item-edited="itemEdited"
  @edit-cancelled="editCancelled">
</to-do-item-edit-form>

12.6.5 Añadir la función de eliminación de tareas

Ahora podemos alternar entre el formulario de edición y la casilla de verificación. Sin embargo, no hemos gestionado la actualización del array ToDoItems en ToDoList. Para solucionarlo, necesitamos escuchar el evento item-edited y actualizar la lista según corresponda. También queremos gestionar el evento de eliminación para poder eliminar elementos de la lista de tareas.

Agregue los siguientes métodos nuevos a su objeto ToDoList de componente , debajo de los métodos existentes dentro de la propiedad methods:

1
2
3
4
5
6
7
8
deleteToDo(toDoId) {
  const itemIndex = this.ToDoItems.findIndex((item) => item.id === toDoId);
  this.ToDoItems.splice(itemIndex, 1);
},
editToDo(toDoId, newLabel) {
  const toDoToEdit = this.ToDoItems.find((item) => item.id === toDoId);
  toDoToEdit.label = newLabel;
}

A continuación, agregaremos los escuchas de eventos para los eventos item-deletedy item-edited:

  • Para item-deleted, necesitarás pasar el item.id al método.
  • Para item-edited, deberás pasar la variable especial item.id$event. Esta es una variable especial de Vue que se usa para pasar datos de eventos a los métodos. Al usar eventos HTML nativos (como click), esto pasará el objeto de evento nativo a tu método.

Actualice la llamada <to-do-item></to-do-item> dentro de la plantilla ToDoList.vue para que se vea así:

1
2
3
4
5
6
7
8
<to-do-item
  :label="item.label"
  :done="item.done"
  :id="item.id"
  @checkbox-changed="updateDoneStatus(item.id)"
  @item-deleted="deleteToDo(item.id)"
  @item-edited="editToDo(item.id, $event)">
</to-do-item>

¡Y ahí lo tienes! ¡Ahora deberías poder editar y eliminar elementos de la lista!


12.6.6 Corrección de un error con el estado isDone

Esto va genial hasta ahora, pero hemos introducido un error al añadir la función de edición. Prueba esto:

  1. Marque (o desmarque) una de las casillas de verificación de tareas pendientes.
  2. Presione el botón "Editar" para ese elemento de la tarea.
  3. Cancele la edición presionando el botón "Cancelar".

Observa el estado de la casilla de verificación después de cancelar: la aplicación no solo ha olvidado el estado de la casilla, sino que el estado de "completado" de esa tarea ahora está desfasado. Si intentas marcarla (o desmarcarla) de nuevo, el recuento de completados cambiará al revés de lo esperado. Esto se debe a que el valor interno isDone de data solo se proporciona un valor a this.done al cargar el componente.

Afortunadamente, solucionar este problema es bastante fácil: podemos hacerlo convirtiendo nuestro elemento isDone de datos en una propiedad calculada, otra ventaja de las propiedades calculadas es que preservan la reactividad , lo que significa (entre otras cosas) que su estado se guarda cuando la plantilla cambia, como ocurre ahora con la nuestra.

Entonces, implementemos la solución en ToDoItem.vue:

Primero eliminar la siguiente línea del interior de nuestra propiedad data():

isDone: this.done,

Segundo agregue el siguiente bloque debajo del bloque data() { }:

1
2
3
4
5
computed: {
    isDone() {
    return this.done;
    }
},

Ahora, cuando guardes y vuelvas a cargar, verás que el problema está resuelto: el estado de la casilla de verificación ahora se conserva cuando cambias entre plantillas de elementos de tarea pendiente.

12.6.7 Comprender la maraña de acontecimientos

Una de las partes más potencialmente confusas es la maraña de eventos estándar y personalizados que hemos usado para activar toda la interactividad en nuestra aplicación. Para comprender esto mejor, conviene crear un diagrama de flujo, una descripción o un diagrama que muestre qué eventos se emiten y dónde, dónde se escuchan y qué sucede al activarse.

Aplicación.vue

<to-do-form> escucha para:

  • evento emitido todo-added por el método onSubmit() dentro del componente ToDoForm al enviar el formulario. Resultado : el método invocado addToDo() para añadir un nuevo elemento de la lista de tareas a la matríz ToDoItems.

<to-do-item> escucha para:

  • evento emitio checkbox-changed por la casilla de verificación <input> dentro del componenete ToDoItem al estar marcada o desmarcada. Resultado: el método invicado updateDoneStatus() para actualizar el estado de finalización del elemento de la tarea asociada.
  • Evento emitido item-deleted por el método deleteToDo() dentro del componente ToDoItem al presionar el botón "Eliminar". Resultado: método invocado deleteToDo() para eliminar el elemento de la tarea asociada.
  • Evento emitido item-edited por el método itemEdited() dentro del componente ToDoItem cuando se escucha correctamente. Sí, se trata de una cadena de dos eventos diferentes item-edited. Resultado: método invocado editToDo() para actualizar la etiqueta asociada al elemento todo item.

ToDoForm.vue

<form> escucha el evento submit. Resultado: el método onSubmit() es invocado, que verifica que la nueva etiqueta no esté vacía, emite el evento todo-added (que se escucha dentro de ToDoList.vue y, finalmente, borra la nueva etiqueta <input>.

ToDoItem.vue

El <input> of type="checkbox" escucha eventos change. Resultado: el evento  checkbox-changed emitido cuando se marca o desmarca la casilla de verificación (que luego se escucha internamente ToDoList.vue.

El <button> "Editar" detecta el evento click. Resultado : se invoca el método toggleToItemEditForm(), que cambia this.isEditingtrue, lo que a su vez muestra el formulario de edición del elemento de la lista al volver a renderizar.

el <button> "Eliminar" detecta el evento click. Resultado : se invoca el método deleteToDo(), que emite el evento item-deleted (que se detecta internamente ToDoList.vue).

<to-do-item-edit-form> escucha para:

  • El evento item-edited emitido por el método onSubmit() dentro del componente ToDoItemEditForm cuando el formulario se envía correctamente. Resultado: se invoca el método itemEdited(), que emite el evento item-edited (que se escucha dentro de ToDoList.vue) y reestablece el valor de  this.isEditing a  false, de modo que el formulario de edición ya no se muestra al volver a renderizar.

  • El evento edit-cancelled emitido por el método onCancel() dentro del componenete ToDoItemEditForm al hacer clic en el botón "Cancelar". Resultado: se invoa al método editCancelled(), y this.isEditing vuelve a establecerse en false, por lo que el formulario de edición ya no se muestra al volver a renderizar.

[ToDoItemEditForm.vue]

<form> espera el evento submit. Resultado: se invoca el método onSubmit(), que verifica si el nuevo valor de etiqueta no está en blanco ni es igual al anterior. De ser así, emite el evento item-edited (que luego se escucha dentro de ToDoIntem).

El botón "Cancelar" detecta el evento click. Resultado: se invoca el método onCancel(), que emite el evento edit-cancelled (que se detecta dentro de ToDoItem.vue).

12.6.8 Resumen

Este artículo ha sido bastante intenso y hemos cubierto muchos temas. Ahora tenemos la función de editar y eliminar en nuestra aplicación. Nos acercamos al final de nuestras prácticas sobre Vue. La última función que veremos es la gestión del foco, o dicho de otro modo, cómo podemos mejorar la accesibilidad del teclado de nuestra aplicación.

He revisado la Unidad 12: Renderizado Condicional en Vue.js y todo está bien estructurado. Ahora, te propongo una actividad final basada en este tema, específicamente para el Blog.


Actividad Final: Implementación de Renderizado Condicional en el Blog

📌 Objetivo: Implementar la funcionalidad de mostrar diferentes estados de una entrada del blog (por ejemplo, "Cargando", "Error" y "Contenido cargado") utilizando las directivas de renderizado condicional (v-if, v-else-if, v-else) en Vue.js.

📌 Pasos a seguir:
1️⃣ Crear un estado status en BlogList.vue que determine si los datos están siendo cargados, si hubo un error o si ya se cargaron correctamente.
2️⃣ Utilizar las directivas v-if, v-else-if y v-else para mostrar diferentes mensajes en función del valor de status.
3️⃣ Agregar un button para simular los estados de carga y error mediante un cambio manual en el estado de status.


🔒 Solución Final (oculta inicialmente)

Código de BlogList.vue con Renderizado Condicional:

Solución a la Actividad Final
<template>
<div class="container mt-4">
    <h2 class="text-center">Entradas del Blog</h2>

    <!-- Estado: Cargando -->
    <p v-if="status === 'loading'">Cargando...</p>

    <!-- Estado: Error -->
    <p v-else-if="status === 'error'">Error al cargar los datos.</p>

    <!-- Estado: Contenido Cargado -->
    <div v-else>
    <ul>
        <li v-for="(post, index) in posts" :key="index">
        {{ post.titulo }}
        </li>
    </ul>
    </div>

    <button @click="simulateLoading">Simular Carga</button>
    <button @click="simulateError">Simular Error</button>
</div>
</template>

<script>
export default {
data() {
    return {
    status: 'loading', // 'loading', 'error', 'loaded'
    posts: [
        { titulo: 'Primer Post', contenido: 'Contenido de la primera entrada' },
        { titulo: 'Segundo Post', contenido: 'Contenido de la segunda entrada' }
    ]
    };
},
methods: {
    simulateLoading() {
    this.status = 'loading';
    setTimeout(() => {
        this.status = 'loaded';
    }, 2000);
    },
    simulateError() {
    this.status = 'error';
    }
}
};
</script>

Explicación de la solución:

  • v-if="status === 'loading'": Muestra el mensaje "Cargando..." cuando el estado es 'loading'.
  • v-else-if="status === 'error'": Muestra un mensaje de error cuando el estado es 'error'.
  • v-else: Muestra el contenido de las entradas cuando el estado es 'loaded'.
  • Simulación de estados: Los botones permiten cambiar el estado a 'loading' o 'error', simulando el comportamiento de una aplicación real.

Objetivo de la actividad:

Con esta actividad, los alumnos aprenderán a usar el renderizado condicional de Vue.js para gestionar la visualización dinámica de contenido basado en el estado de la aplicación, lo que mejora la experiencia del usuario al interactuar con la interfaz.