Unidad 13: Vue: Ciclo de vida para la gestión del foco.
Ya casi terminamos con Vue. La última funcionalidad que revisaremos es la gestión del foco, o dicho de otro modo, cómo podemos mejorar la accesibilidad del teclado de nuestra aplicación. Veremos cómo usar las referencias de Vue para gestionar esto: una función avanzada que permite acceder directamente a los nodos DOM subyacentes bajo el DOM virtual, o acceder directamente desde un componente a la estructura DOM interna de un componente secundario.
13.1. Los problemas de accesibilidad del teclado.
Si bien contamos con la función de edición, no ofrecemos una experiencia óptima para quienes no usan el ratón. En concreto, cuando un usuario activa el botón "Editar", lo eliminamos del DOM, pero no movemos el foco del usuario a ninguna parte; simplemente desaparece. Esto puede resultar desorientador para quienes usan el teclado o no tienen visión.
Para entender lo que está sucediendo actualmente:
- Recarga la página y pulsa
Tab. Deberías ver un contorno destacado en la entrada para añadir nuevas tareas. - Pulsa
Tabde nuevo. El enfoque debería moverse al botón "Añadir". - Presiónalo de nuevo y aparecerá en la primera casilla de verificación. Una vez más, el foco debería estar en el primer botón "Editar".
- Active el botón "Editar" pulsando
Enter. La casilla de verificación se reemplazará con nuestro componente de edición, pero el contorno del foco desaparecerá.
Este comportamiento puede ser molesto. Además, lo que ocurre al volver a pulsar Tab varía según el navegador. De igual forma, si guardas o cancelas la edición, el foco desaparecerá al volver a la vista sin editar.
Para ofrecer una mejor experiencia a los usuarios, añadiremos código para controlar el enfoque, de modo que se establezca en el campo de edición al mostrar el formulario. También queremos volver a poner el enfoque en el botón "Editar" cuando un usuario cancele o guarde su edición. Para establecer el enfoque, necesitamos comprender mejor el funcionamiento interno de Vue.
13.2. Referencias en Vue.
Vue, al igual que otros frameworks, utiliza un DOM virtual (VDOM) para gestionar los elementos. Esto significa que Vue mantiene una representación de todos los nodos de nuestra aplicación en memoria. Las actualizaciones se realizan primero en los nodos en memoria y, posteriormente, todos los cambios necesarios en los nodos de la página se sincronizan en lote.
Dado que leer y escribir nodos DOM reales suele ser más costoso que escribir nodos virtuales, esto puede resultar en un mejor rendimiento. Sin embargo, también significa que, a menudo, no se deben editar los elementos HTML directamente a través de las API nativas del navegador (como Document.getElementById) al usar frameworks, ya que esto provoca que el VDOM y el DOM real se desincronicen.
En cambio, si necesita acceder a los nodos DOM subyacentes (por ejemplo, al establecer el foco), puede usar referencias de Vue. Para componentes Vue personalizados, también puede usar referencias para acceder directamente a la estructura interna de un componente secundario; sin embargo, esto debe hacerse con precaución, ya que puede dificultar la comprensión del código.
Para usar una referencia en un componente, se agrega un atributo ref al elemento al que se desea acceder, con un identificador de cadena para el valor del atributo. Es importante tener en cuenta que una referencia debe ser única dentro del componente. Dos elementos renderizados simultáneamente no deben tener la misma referencia.
Agregar una referencia a nuestra aplicación
Ahora, añadamos una referencia a nuestro botón "Editar" en ToDoItem.vue. Actualicémoslo así:
Para acceder al valor asociado a nuestra referencia, usamos la propiedad $ref proporcionada en la instancia de nuestro componente. Para ver el valor de la referencia al hacer clic en el botón "Editar", agregamos un console.log() a nuestro método toggleToItemEditForm(), como se muestra a continuación:
<button> HTML referenciado en su consola.
13.3 EL método $nextTick() de Vue
Queremos que el botón "Editar" reciba el foco cuando el usuario guarde o cancele su edición. Para ello, necesitamos gestionar el foco en los métodos itemEdited()y editCancelled() en el componente ToDoItem.
Para mayor comodidad, cree un nuevo método llamado focusOnEditButton(), que no acepte argumentos. Dentro de él, asigne su ref a una variable y luego llame al método focus() en la referencia.
A continuación, agregue una llamada this.focusOnEditButton() al final de los métodos itemEdited() y editCancelled():
Intenta editar y luego guardar/cancelar una tarea con el teclado. Notarás que el foco no se establece, así que aún tenemos un problema por resolver. Si abres la consola, verás un error como "no se puede acceder a la propiedad "foco", editButtonRef no está definido". Esto parece extraño. La referencia de tu botón estaba definida al activar el botón "Editar", pero ahora no. ¿Qué ocurre?
Recuerda que al cambiar isEditing a true, ya no se renderiza la sección del componente con el botón "Editar". Esto significa que no hay ningún elemento al que vincular la referencia, por lo que se convierte en undefined.
Quizás ahora estés pensando: "¿Acaso no configuramos isEditing=false antes de intentar acceder a ref, por lo tanto, no debería v-if mostrar el botón?". Aquí es donde entra en juego el DOM virtual. Dado que Vue intenta optimizar y procesar los cambios por lotes, no actualizará el DOM inmediatamente cuando configuremos isEditing a false. Por lo tanto, cuando llamamos a focusOnEditButton(), el botón "Editar" aún no se ha renderizado.
En su lugar, debemos esperar hasta que Vue complete el siguiente ciclo de actualización del DOM. Para ello, los componentes de Vue cuentan con un método especial llamado $nextTick(). Este método acepta una función de devolución de llamada, que se ejecuta después de las actualizaciones del DOM.
Dado que el método focusOnEditButton() debe invocarse después de que se haya actualizado el DOM, podemos envolver el cuerpo de la función existente dentro de una llamada $nextTick().
```javascript "1" focusOnEditButton() { this.$nextTick(() => { const editButtonRef = this.$refs.editButton; editButtonRef.focus(); }); }
Ahora, al activar el botón "Editar" y luego cancelar o guardar los cambios con el teclado, el enfoque debería volver al botón "Editar". ¡Éxito!
## **13.4 Métodos del ciclo de vida de Vue**
A continuación, necesitamos mover el foco al elemento `<input>`del formulario de edición al hacer clic en el botón "Editar". Sin embargo, dado que nuestro formulario de edición se encuentra en un componente diferente al botón "Editar", no podemos simplemente establecer el foco dentro del controlador de eventos de clic del botón "Editar". En su lugar, podemos usar el hecho de que eliminamos y volvemos a montar nuestro componente `ToDoItemEditForm` al hacer clic en el botón "Editar" para gestionar esto.
¿Cómo funciona esto? Los componentes de Vue pasan por una serie de eventos, conocidos como ciclo de vida . Este ciclo abarca desde antes de que los elementos *se creen* y se añadan al VDOM ( *montado* ) hasta que se eliminan del VDOM ( *destruido* ).
Vue permite ejecutar métodos en varias etapas de este ciclo de vida mediante métodos de ciclo de vida . Esto puede ser útil para tareas como la obtención de datos, donde podría ser necesario obtenerlos antes de que el componente se renderice o después de que cambie una propiedad. A continuación, se muestra la lista de métodos de ciclo de vida, en el orden en que se ejecutan.
1. `beforeCreate()`--- Se ejecuta antes de crear la instancia del componente. Los datos y eventos aún no están disponibles.
2. `created()`--- Se ejecuta después de inicializar el componente, pero antes de añadirlo al VDOM. Aquí es donde suele tener lugar la obtención de datos.
3. `beforeMount()`--- Se ejecuta después de que se compila la plantilla, pero antes de que el componente se represente en el DOM real.
4. `mounted()`--- Se ejecuta después de montar el componente en el DOM. Puede acceder a `refs` aquí.
5. `beforeUpdate()`--- Se ejecuta siempre que cambian los datos de su componente, pero antes de que los cambios se representen en el DOM.
6. `updated()`--- Se ejecuta siempre que los datos de su componente hayan cambiado y después de que los cambios se representen en el DOM.
7. `beforeDestroy()`--- Se ejecuta antes de que se elimine un componente del DOM.
8. `destroyed()`--- Se ejecuta después de que se haya eliminado un componente del DOM.
9. `activated()`--- Solo se usa en componentes envueltos en una etiqueta `keep-alive` especial. Se ejecuta después de activar el componente.
10. `deactivated()`--- Solo se usa en componentes envueltos en una etiqueta `keep-alive` especial. Se ejecuta después de desactivar el componente.
!!! note "Diagrama de Vue"
**nota:** La documentación de Vue proporciona un [diagrama útil para visualizar cuándo se activan estos ganchos](https://vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagram). Este artículo del [blog de la comunidad de DigitalOcean profundiza en los métodos del ciclo de vida](https://www.digitalocean.com/community/tutorials/vuejs-component-lifecycle) .
Ahora que hemos repasado los métodos del ciclo de vida, usemos uno para activar el foco cuando se monta nuestro componente `ToDoItemEditForm` .
En `ToDoItemEditForm.vue`, adjuntarlo `ref="labelInput"`al `<input>`elemento, de la siguiente manera:
```html linenums="1"
<input
:id="id"
ref="labelInput"
type="text"
autocomplete="off"
v-model.lazy.trim="newName" />
A continuación, agregue una propiedad mounted() dentro de su objeto componente. Tenga en cuenta que no debe colocarse dentro de la propiedad methods, sino en el mismo nivel jerárquico que props, data()y methods.Los métodos de ciclo de vida son métodos especiales que se ubican por sí solos, no junto a los métodos definidos por el usuario. Esto no debería aceptar entradas. Tenga en cuenta que no puede usar una función de flecha aquí, ya que necesitamos acceder a this para acceder a nuestra referencia labelInput.
Dentro de tu método mounted(), asigna tu referencia labelInput a una variable y luego llama a la función focus() de la referencia. No es necesario usar $nextTick() aquí, ya que el componente ya se agregó al DOM al llamarlo mounted().
Ahora, cuando active el botón "Editar" con su teclado, el foco debería moverse inmediatamente a la edición <input>.
Manejo del foco al eliminar elementos de las tareas pendientes
Hay un aspecto más que debemos considerar en la gestión del enfoque: cuando un usuario elimina una tarea pendiente. Al hacer clic en el botón "Editar", conviene mover el enfoque al cuadro de texto del nombre de la edición y volver al botón "Editar" al cancelar o guardar desde la pantalla de edición.
Sin embargo, a diferencia del formulario de edición, no tenemos una ubicación clara a la que dirigir el foco cuando se elimina un elemento. También necesitamos una forma de proporcionar a los usuarios de tecnología de asistencia información que confirme la eliminación de un elemento.
Ya estamos rastreando el número de elementos en el encabezado de nuestra lista (la etiqueta <h2> en la ToDoList.vue) y está asociado con nuestra lista de tareas pendientes. Esto lo convierte en un lugar razonable al que dirigir el enfoque al eliminar un nodo.
Primero, necesitamos agregar una referencia al encabezado de nuestra lista. También necesitamos agregarle un tabindex="-1" al elemento, esto permite que el elemento tenga enfoque programático (es decir, se puede enfocar mediante JavaScript), cuando por defecto no lo tiene.
Dentro del componente ToDoList.vue, actualiza tu etiqueta <h2> como sigue:
Nota: tabindex Es una herramienta muy potente para gestionar ciertos problemas de accesibilidad. Sin embargo, debe usarse con precaución. Su uso excesivo tabindex="-1" puede causar problemas a todo tipo de usuarios, así que úsela solo cuando sea estrictamente necesaria. Además, casi nunca debe usar tabindex> = 0, ya que puede causar problemas a los usuarios, ya que puede provocar discrepancias en el flujo del DOM y el orden de tabulación, o añadir elementos no interactivos al orden de tabulación. Esto puede resultar confuso para los usuarios, especialmente para quienes utilizan lectores de pantalla y otras tecnologías de asistencia.
Ahora que tenemos una ref y hemos notificado a los navegadores que podemos enfocar <h2> programáticamente, necesitamos poner el foco sobre él. Al final de deleteToDo(), usa la referencia listSummary
para enfocar <h2>. Dado que <h2> siempre se renderiza en la aplicación, no necesitas preocuparte por usar $nextTick()métodos de ciclo de vida o para gestionar el enfoque.
Ahora, al eliminar un elemento de la lista, el enfoque debería moverse al encabezado de la lista. Esto debería proporcionar una experiencia de enfoque razonable para todos nuestros usuarios.
13.5 Resumen
¡Eso es todo para la gestión del foco y por nuestra app! ¡Felicidades por completar todo nuestro tutorial de Vue! En el próximo artículo, completaremos el tema con recursos adicionales para que puedas profundizar en tu aprendizaje de Vue.
Version app
Nota: Si necesita comparar su código con nuestra versión, puede encontrar una versión final del código de ejemplo de la aplicación Vue en nuestro repositorio todo-vue. Para ver una versión en vivo, consulte https://mdn.github.io/todo-vue/.