Nessa seção vamos fazer um simples todo usando o angular
Publicado em 28 de abril de 2026
Nesse post vou mostrar como criei uma lista de tarefas simples no Angular.
A ideia era bem direta: adicionar tarefa, marcar como completa, remover e contar quantas ainda estão pendentes.
Parece simples, mas no meio do caminho apareceram algumas pegadinhas 😅
Primeiro criei um formulário usando FormBuilder:
tasks: FormGroup;
constructor(private fb: FormBuilder) {
this.tasks = fb.group({
task: ['', [Validators.minLength(3)]],
isComplete: false
});
}
No HTML ficou assim:
<form [formGroup]="tasks">
<div>
<label for="task">Tarefa</label>
<input formControlName="task" type="text" name="task">
</div>
<button type="button" (click)="sendForm()">Ok</button>
</form>
No começo eu tentei fazer isso:
this.tasks.value === ''
Mas isso está errado, porque value não é uma string, e sim um objeto.
Então o correto foi pegar o campo específico:
const task = this.tasks.value.task?.trim();
if (!task) return;
const obj = {
task,
isComplete: false
};
Eu poderia fazer assim:
this.taskList.push(obj);
Mas isso altera o array original.
Então usei a versão imutável:
this.taskList = [...this.taskList, obj];
Aqui eu basicamente pego tudo que já existia e adiciono o novo item no final.
@for (task of taskList; track task; let i = $index) {
<ul>
<li>{{ task.task }}</li>
</ul>
}
sendComplete(index: number) {
this.taskList = this.taskList.map((task, i) => {
if (i !== index) return task;
return {
...task,
isComplete: !task.isComplete
};
});
}
A lógica é:
isCompleteremoveTask(index: number) {
this.taskList = this.taskList.filter((_, i) => i !== index);
}
Aqui eu removo o item pelo índice.
get pendingTasks() {
return this.taskList.filter(task => !task.isComplete).length;
}
No HTML:
<p>Pendentes: {{ pendingTasks }}</p>
Um detalhe: não pode colocar isso dentro do @for, senão ele repete várias vezes.
<form [formGroup]="tasks">
<div>
<label for="task">Tarefa</label>
<input formControlName="task" type="text" name="task">
</div>
<button type="button" (click)="sendForm()">Ok</button>
</form>
<p>Pendentes: {{ pendingTasks }}</p>
@for (task of taskList; track task; let i = $index) {
<ul>
<li [ngClass]="{ active: task.isComplete }">
{{ task.task }}
</li>
<button type="button" (click)="sendComplete(i)">
{{ task.isComplete ? 'Completo' : 'Incompleto' }}
</button>
<button type="button" (click)="removeTask(i)">
Remover
</button>
}
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'app-todo',
imports: [ReactiveFormsModule, CommonModule],
templateUrl: './todo.html',
styleUrl: './todo.scss',
})
export class Todo {
tasks: FormGroup;
taskList: any[] = [];
constructor(private fb: FormBuilder) {
this.tasks = fb.group({
task: ['', [Validators.minLength(3)]],
isComplete: false
});
}
sendForm() {
const task = this.tasks.value.?.();
(!task) ;
obj = {
task,
:
};
. = [...., obj];
..({
: ,
:
});
}
() {
. = ..( {
(i !== index) task;
{
...task,
: !task.
};
});
}
() {
. = ..( i !== index);
}
() {
..( !task.).;
}
}
Mutável seria assim:
this.taskList.push(obj);
Ou:
this.taskList.splice(index, 1);
Ou ainda:
this.taskList[index].isComplete = true;
Isso funciona, mas muda o array original.
A forma imutável cria uma nova lista com a alteração:
this.taskList = [...this.taskList, obj];
this.taskList = this.taskList.filter((_, i) => i !== index);
this.taskList = this.taskList.map((task, i) => {
if (i !== index) return task;
return {
...task,
isComplete: !task.isComplete
};
});
No começo parece mais complicado, mas a ideia é simples:
ao invés de mexer direto na lista antiga, eu crio uma nova lista já atualizada.
Isso ajuda o Angular a perceber melhor as mudanças e evita efeitos colaterais em projetos maiores.
Um erro foi tentar comparar o formulário inteiro com string vazia:
this.tasks.value === ''
Outro foi usar *ngClass, sendo que o correto é:
<li [ngClass]="{ active: task.isComplete }">
Também coloquei o contador de pendentes dentro do @for, e ele repetia para cada item da lista.
São detalhes pequenos, mas que na prática travam bastante.
Adicionar tarefa:
cria objeto e adiciona na lista
Completar tarefa:
usa map para alterar só o item clicado
Remover tarefa:
usa filter para remover pelo índice
Contar pendentes:
usa filter + length
Imutabilidade:
cria uma nova lista ao invés de alterar a original
Esse exercício parecia simples, mas me fez revisar muita coisa importante.
Trabalhei com formulário, lista, evento de clique, classe condicional, contagem de pendentes e imutabilidade.
No fim, percebi que errar detalhe faz parte, até porque muita coisa só fixa quando a gente pratica e quebra a cabeça.
Carregando comentários...