Tome el curso de React y para hacer esto mismo haces un tremendo chorizo solo para eliminar y agregar elementos y aquí se siente super intuitivo manipular el dom
•Event Binding: (click)="clickHandler()" (Paréntesis para eventos)
•Property Binding: [disabled]="isDisabled" (Corchetes para propiedades)
•Interpolation: {{ expression }} (Dobles llaves para interpolar valores)
•$event: Representa el evento y se usa para pasar información sobre el evento al método que se ejecuta.
Me esta gustando demasiado Angular.
Este fue el código que use para lograr este resultado
<li
[class.completed]="task.completed"
*ngFor="let task of tasks(); index as i"
>
<div class="view">
<input
class="toggle"
type="checkbox"
[checked]="task.completed"
(click)="toggleChecked(i)"
/>
<label>{{ task.title }}</label>
<button class="destroy"></button>
</div>
<input class="edit" [value]="task.title" />
</li>
Hice este codigo para marcarlo como completado, estaria mejor así o usar map? entiendo que map hace una busqueda pero si tenemos el index supongo que no habria necesidad de usar el map y seria mejor asignarlo directamente? o hay alguna otra forma más optima de hacerlo?
toggleChecked(index: number){this.tasks.update((tasks)=> tasks.map((task, i)=> i === index ?{...task,complete:!task.complete}: task
));}
con eso también le agregué un poco de enjundia, no es mucho pero es trabajo honesto. confirmar si se marca la tarea como realizada o no:
(click)="updateTask(i,task.completed)"
updateTask(index:number,status:boolean){ const est = (status==false)?"completa":"incompleta"; if(confirm("Confirma que quiere marcar como "+est+" ?")){ this.tasks.update((tasks) => tasks.map((task, i) => i === index ? { ...task, completed: !task.completed }: task ) ) } }
Hice esto con algo de ayuda:
//home.component.html<sectionclass="todoapp"><headerclass="header"><divclass="container"><h2>My Day</h2><p>All my tasks in one place</p> <input
class="new-todo"
placeholder="Type new todo"
autofocus
name="filterName"
type="text"
(keydown)="changeHandler($event)"
onfocus="this.value=''"
/>
</div></header><divclass="container todoapp-wrapper"> <!-- This section should be hidden by default and shown when there are todos -->
<sectionclass="main"><ulclass="todo-list"> <li
*ngFor='let task of tasks(); index as i'
[ngClass]="{'completed': task.completed}"
>
<divclass="view"> <input
class="toggle"
type="checkbox"
[checked]="task.completed"
(change)="changeTaskStatus(i)"
/>
<label>{{ task.title}}</label> <button class="destroy" (click)="deleteTask(i)"></button></div><inputclass="edit"value="Learn JavaScript"/></li> <!-- <liclass="editing"><divclass="view"><inputclass="toggle"type="checkbox"/><label>Make dishes</label><buttonclass="destroy"></button></div><inputclass="edit"value="Make dishes"/></li> -->
</ul></section> <!-- This footer should be hidden by default and shown when there are todos -->
<footerclass="footer"> <!-- This should be `0 items left` by default -->
<spanclass="todo-count"><strong>0</strong> item left</span> <!-- Remove this if you don't implement routing -->
<ulclass="filters"><li><arouterLink="/"class="selected">All</a></li><li><arouterLink="/pending">Pending</a></li><li><arouterLink="/completed">Completed</a></li></ul> <!-- Hidden if no completed items are left ↓ -->
<buttonclass="clear-completed">Clear completed</button></footer></div></section>//home.component.tsimport{Component, signal }from'@angular/core';import{CommonModule}from'@angular/common';import{Task}from'./../../models/task.model'@Component({selector:'app-home',standalone:true,imports:[CommonModule],templateUrl:'./home.component.html',styleUrl:'./home.component.css'})exportclassHomeComponent{ tasks = signal<Task[]>([{id:Date.now(),title:'Crear proyecto',completed:false,},{id:Date.now(),title:'Crear componentes',completed:false,},{id:Date.now(),title:'Crear interfaz',completed:false,},])getincompleteTasks(){returnthis.tasks().filter(task=>!task.completed);}getcompleteTasks(){returnthis.tasks().filter(task=>!!task.completed);}changeHandler(event:KeyboardEvent){const input = event.targetasHTMLInputElement;if(event.key==='Enter'&& input.value.trim()!==''){const newTask = input.value.trim()this.addTask(newTask); input.value='';}}addTask(title: string){const newTask ={id:Date.now(), title,completed:false,}this.tasks.update((tasks)=>[...tasks, newTask]);}deleteTask(index: number){this.tasks.update((task)=> task.filter((task, position)=> position !== index ))}changeTaskStatus(index: number){this.tasks.update((tasks)=> tasks.map((task, i)=> i === index ?{...task,completed:!task.completed}: task
))}}
Comparto la pequeña solución que le di, recibo cualquier critica constructiva.
Me llevo que el typing en Angular no solo previene errores, sino que guía al desarrollador, alertando cuando se intenta usar propiedades inexistentes o insertar datos que no cumplen con el modelo definido.
📍Separar responsabilidades en los métodos
Me llevo que es una buena práctica separar la lógica: un método para capturar el input y otro para construir e insertar la nueva tarea con la estructura correcta dentro de la Signal.
📍Uso de interfaces para tipar el estado
Me llevo que crear una interfaz (model) permite definir claramente qué propiedades debe tener una tarea (id, título, estado), evitando errores y haciendo el código más seguro y mantenible.
📍Importancia de definir una estructura de datos
Me llevo que mezclar strings, números y objetos en un mismo array no es una buena práctica, y que lo correcto es definir una estructura homogénea para todos los elementos de la lista.
📍Signals con estructuras de datos complejas
Me llevo que una Signal no solo puede contener valores primitivos, sino también arrays de objetos, que es el caso más común en aplicaciones reales como listas de tareas.
yo lo hice de la siguiente manera: este es ele vento de mi componente:
Para general un id mas realista utilice el siguiente codigo: { id: crypto.randomUUID(), title: 'Instalar el Angular CLI', completed: false, },
{id: crypto.randomUUID(),title:'Instalar el Angular CLI',completed:false,},```y el interface:
```js
exportinterfaceTask{id: string;title: string;completed: boolean;}
🚩 Mi solución al reto
Enlace a mi repo de GitHub:
No he visto la siguiente clase pero en este punto yo lo resolví así:
<li *ngFor="let task of tasks(); index as i"[class]="getClass(task)"><div class="view"><input class="toggle" type="checkbox"(click)="clickHandler(task, $event)"/><label>{{ task.title}}</label><button class="destroy"(click)="deleteTask(i)"></button></div><input class="edit" value="Buy a unicorn"/></li>
Y por la parte de TS quedo de la siguiente manera: