Eduardo Razo Cobián
Mi solución:
Aplicar clases dinámicas en Angular te permite cambiar estilos según el estado de un formulario o componente, sin mover elementos del DOM ni romper la experiencia visual. Esta técnica usa class binding y validadores reactivos para mostrar mensajes de error de forma elegante, ideal para desarrolladores frontend que buscan formularios usables y limpios.
Por qué evitar ngIf para mostrar errores en formularios
Usar *ngIf parece la solución obvia: si el control es inválido, renderizas el mensaje; si es válido, lo ocultas. El problema aparece cuando pruebas el formulario en el navegador.
Cuando *ngIf evalúa la condición, renderiza o elimina el elemento del DOM. Eso significa que el espacio que ocupa el mensaje de error aparece y desaparece, empujando los inputs que están debajo. Si un usuario está llenando un formulario y los campos saltan cada vez que aparece un error, la usabilidad se rompe.
¿Qué hace ngIf en Angular? Renderiza o elimina un elemento del DOM según una condición booleana. No lo oculta visualmente, lo quita por completo, lo que puede mover otros elementos de la página.
La alternativa es mantener el elemento siempre en el DOM y solo activar una clase CSS que controle su visibilidad con opacity.
Cómo funciona el class binding con [class.nombre] en Angular
El class binding es una forma de property binding que activa o desactiva una clase CSS según una expresión booleana. La sintaxis es directa:
html <span class="message-error" [class.show]="nameControl.invalid && nameControl.touched"> Nombre inválido </span>
Aquí, show es el nombre de la clase que quieres activar. Puede ser cualquier nombre que tengas definido en tu CSS. La clase se agrega al elemento solo cuando la condición se cumple [03:45].
Cómo configurar los validadores required y minLength
En el controlador del formulario reactivo, defines las reglas que activarán el estado inválido:
typescript nameControl = new FormControl('', [ Validators.required, Validators.minLength(3) ]);
Validators.required obliga a que el campo no esté vacío.Validators.minLength(3) exige al menos 3 caracteres, similar a cómo se valida la longitud mínima de un password [01:10].touched verifica si el usuario ya interactuó con el input, evitando mostrar errores antes de tiempo.¿Qué es la propiedad touched en un FormControl? Es un booleano que se vuelve
truecuando el usuario hace foco en el input y luego sale de él. Sirve para no mostrar errores antes de que la persona haya interactuado con el campo.
Cómo ocultar el mensaje con opacity y transition
En el CSS, el mensaje siempre está en el DOM, pero invisible por defecto:
css .message-error { background: repeating-linear-gradient(...); color: white; padding: 8px; opacity: 0; transition: all linear 0.5s; }
.message-error.show { opacity: 1; }
La clave está en la transition: si olvidas la s en el tiempo (por ejemplo escribir 5 en lugar de 0.5s), la animación no funciona [06:50]. El detalle de incluir la unidad de tiempo es lo que activa la animación suave.
Qué ventajas tiene usar clases dinámicas frente a renderizado condicional
La diferencia entre ambos enfoques se nota apenas pruebas el formulario.
opacity da una transición visual mucho más limpia.show se agrega y quita dinámicamente según la validación.¿Cuándo usar class binding y cuándo ngIf? Usa class binding cuando solo cambia el estilo o visibilidad de un elemento que debe permanecer en el layout. Usa
*ngIfcuando el elemento realmente no debe existir en ciertos estados, como bloques de contenido completos.
Cómo aplicar este patrón a una lista de tareas
El mismo principio sirve para reflejar el estado de una tarea. Si tienes una lista y cada item tiene un estado completed, puedes activar una clase visual condicional:
html
<li [class.completed]="task.completed">{{ task.title }}</li>Cuando task.completed sea true, la clase completed se aplica y puedes definir en CSS un estilo como text-decoration: line-through o un color atenuado. Lo mismo aplica para un estado de edición: si el usuario empieza a editar, activas otra clase dinámica que cambie la apariencia del item.
Este patrón mantiene tu HTML declarativo y tu CSS responsable de la presentación, sin lógica imperativa para agregar o quitar clases manualmente.
¿Cómo resolviste el reto del estado completed en tu lista de tareas? Cuéntalo en los comentarios.
Tambien lo hice de esa manera.
<li *ngFor="let taskP of tasksPractices(); index as i" [class.completed]="taskP.completed === true"> <div class="view"> <!-- <input class="toggle" type="checkbox" (click)="markCompleted(i, $event)"/> --> <input class="toggle" type="checkbox" (change)="updateTask(i)" [checked]="taskP.completed"/> <label>{{ taskP.title }}</label> <button class="destroy" (click)="deletedTasks(i)"></button> </div> <input class="edit" value="Buy a unicorn" /> </li>
Eduardo Razo Cobián
Mi solución:
Raúl Adolfo Sánchez Rodríguez
Cindy Melissa Velásquez Sánchez
Mi solución:
JHONATAN AGUILERA GARCIA
Mi solución al reto
<li *ngFor="let task of tasks(); index as i" [ngClass]="{'completed': task.completed }"> <div class="view"> <input class="toggle" type="checkbox" [checked]="task.completed" (change)="updateTask(i)" /> <label>{{ task.title }}</label> <button class="destroy" (click)="deleteTask(i)"></button> </div> <input class="edit" value="Buy a unicorn" /> </li>
Dario Bublitz
📍Animaciones suaves y UX profesional
Me llevo que combinar clases dinámicas con transiciones CSS permite mostrar mensajes de error de forma suave y profesional, mejorando notablemente la experiencia del usuario sin complicar la lógica.
Dario Bublitz
📍Buena práctica: mostrar errores con clases y CSS
Me llevo que una mejor práctica es mantener el elemento en el DOM y controlar su visibilidad con CSS (por ejemplo opacity) activando una clase como show, evitando movimientos innecesarios en la interfaz.
Dario Bublitz
📍Evitar problemas de usabilidad con ngIf
Me llevo que usar ngIf para mostrar errores puede generar saltos visuales en el formulario, afectando la experiencia del usuario cuando los elementos se mueven al aparecer o desaparecer.
Dario Bublitz
📍Validaciones con FormControl y estilos reactivos
Me llevo que los FormControl exponen propiedades como .invalid y .touched, que pueden usarse para mostrar estados visuales solo cuando el usuario ya interactuó con el input y la validación falla.
Dario Bublitz
📍Manejo dinámico de clases en Angular
Me llevo que Angular permite activar o desactivar clases CSS dinámicamente usando Property Binding con class.nombreClase, lo que da un control mucho más flexible sobre los estilos según el estado del componente.
Jose Pintado
<li class="view" [class.completed]="tarea.completed" *ngFor="let tarea of tareas(); index as i">
Eminson Mendoza
La clase se centró en cómo activar dinámicamente clases en Angular utilizando validaciones en formularios. Se explicó cómo estilizar elementos según su estado (válido o no) utilizando validadores como validators.required y validators.minLength. Para mejorar la usabilidad, se recomendó no mover elementos al mostrar mensajes de error, utilizando en su lugar clases con opacidad. Se implementó una animación suave al cambiar la visibilidad del mensaje de error al aplicar dinámicamente la clase correspondiente. Al final, se propuso un reto sobre la gestión de clases según el estado de una tarea.
Saskia Milagros Cornejo Flores
Comparto mi solución al reto.
Rody Josue Ccoyllo Cule
JHONATAN AGUILERA GARCIA
El reto quedo de la siguiente forma
<ul class="todo-list"> <li *ngFor="let task of tasks(); index as i" [class.completed]="task.complated"> <div class="view"> <input class="toggle" type="checkbox" [checked]="task.complated" (change)="updateTask(i)" /> <label>{{task.title}}</label> <button class="destroy" (click)="deleteTask(i)"></button> </div> <input class="edit" value="Learn JavaScript" /> </li> </ul>
Sergio Andrés Vargas Pérez
HTML:
CSS:
Sergio Andrés Vargas Pérez
HTML:
CSS:
Israel Flores
Hola que tal, se me esta presentando algo que no entiendo bien, cuando cargo la pagina por primera vez y entro al campo de nombre para lo del error no me muestra el error hasta que salgo del campo, después de eso ya lo hace reactivamente.
FRANZ VILLARROEL CARDENAS
A mi me pasa lo mismo, pero si te fijas creo que el instructor tiene el mismo problema, porque cada vez que hace la validacion primero hace click en el imput de abajo
HECTOR GERARDO LUNA PULIDO
Hola que tal, creo que es justamente la intencion de que no muestre mensajes de error si el usuario no ha ingresado nada. Supongo que sera mala experiencia para el usuario ver todos los carteles rojos si no hizo nada. A mi entender la propiedad touched del reactiveform se debe modificar cuando pierde el foco (click afuera, tab o si lo modificas con codigo). Si me equivoque en algo espero me corrijan saludos!
Danny Alejandro fernandez gallego
<li *ngFor="let toDo of toDo(); index as i" [class.completed]="toDo.completed">
Danny Alejandro fernandez gallego
esta es mi solución
Mi solución:
⚫CSS:
.tarea.completed{ color: #f78888!important; text-decoration: line-through; }
🔵TS;
Ver elemento label: Se activa la clase de arriba si su propiedad "completed" es TRUE
@for (tareita of tasks2(); track $index){ <li > <div class="view"> <input class="toggle" type="checkbox" (change)="changeCheck($event, $index)" [checked]="tareita.completed"/> <label class="tarea" [class.completed]="tareita.completed">{{tareita.title}}</label> <button class="destroy" (click)="deleteTask($index)"></button> </div> <input class="edit" [value]="tareita" /> </li> }
Les comparto mi solución:
<ul class="todo-list"> <li [class.completed]="tack.completed" *ngFor="let tack of tacks(); index as i"> <div class="view"> <input class="toggle" type="checkbox" [checked]="tack.completed" (click)="onUpdateTack(i)"/> <label>{{tack.title}}</label> <button class="destroy" (click)="deleteTack(i)"></button> </div> <input class="edit" value="Buy a unicorn" /> </li> ```\<ul class="todo-list"> \<li \[class.completed]="tack.completed" \*ngFor="let tack of tacks(); index as i"> \<div class="view"> \<input class="toggle" type="checkbox" \[checked]="tack.completed" (click)="onUpdateTack(i)"/> \<label>{{tack.title}}\</label> \<button class="destroy" (click)="deleteTack(i)">\</button> \</div> \<input class="edit" value="Buy a unicorn" /> \</li>