Opção A: Configuração com Módulos (app.module.ts)

Se seu projeto utiliza um app.module.ts, siga estes passos.

1. Configure o app.module.ts

Para que nosso serviço possa fazer requisições HTTP e para usarmos formulários, precisamos importar os módulos HttpClientModule e FormsModule. Abra src/app/app.module.ts e modifique-o:

// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http'; // 1. Importe
import { FormsModule } from '@angular/forms';           // 2. Importe
 
import { AppComponent } from './app.component';
import { TaskListComponent } from './components/task-list/task-list.component';
 
@NgModule({
  declarations: [
    AppComponent,
    TaskListComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule, // 3. Adicione aos imports
    FormsModule       // 4. Adicione também
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

2. Implemente o TarefaService

Abra src/app/services/tarefa.service.ts e adicione os métodos para realizar as operações CRUD (Create, Read, Update, Delete) na API.

// src/app/services/tarefa.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Tarefa } from '../models/tarefa';
 
@Injectable({
  providedIn: 'root'
})
export class TarefaService {
 
  // URL base da nossa API
  private apiUrl = 'http://localhost:8080/api/tarefas';
 
  // Injetamos o HttpClient para fazer requisições HTTP
  constructor(private http: HttpClient) { }
 
  // READ: Retorna a lista de tarefas
  getTarefas(): Observable<Tarefa[]> {
    return this.http.get<Tarefa[]>(this.apiUrl);
  }
 
  // CREATE: Envia uma nova tarefa para a API
  addTarefa(tarefa: Tarefa): Observable<Tarefa> {
    return this.http.post<Tarefa>(this.apiUrl, tarefa);
  }
 
  // UPDATE: Atualiza uma tarefa existente
  updateTarefa(tarefa: Tarefa): Observable<Tarefa> {
    const url = `${this.apiUrl}/${tarefa.id}`;
    return this.http.put<Tarefa>(url, tarefa);
  }
 
  // DELETE: Deleta uma tarefa pelo seu ID
  deleteTarefa(id: number): Observable<void> {
    const url = `${this.apiUrl}/${id}`;
    return this.http.delete<void>(url);
  }
}

Opção B: Configuração com Componentes Standalone

Se seu projeto não possui um app.module.ts e usa componentes standalone: true, a configuração é um pouco diferente.

1. Configure o Acesso HTTP (A Grande Mudança)

A configuração de provedores globais é feita no arquivo src/app/app.config.ts. É aqui que substituímos o HttpClientModule.

// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http'; // 1. IMPORTE
 
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient() // 2. ADICIONE AQUI
  ]
};

O TarefaService permanece exatamente o mesmo da Opção A, pois ele já é “providedIn: ‘root‘“.

2. Integre o Componente Filho

Em uma aplicação standalone, o AppComponent precisa importar explicitamente os componentes que utiliza. Abra src/app/app.component.ts:

// src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { TaskListComponent } from './components/task-list/task-list.component'; // 1. IMPORTE
 
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    RouterOutlet,
    TaskListComponent // 2. ADICIONE AOS IMPORTS
  ],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  title = 'lista-tarefas-web';
}

Etapa 3: Implementando o Componente TaskListComponent

Com a estrutura e os serviços configurados, a próxima etapa é dar vida ao nosso componente principal.

1. Integrando o Componente no AppComponent

Independente da abordagem (Módulos ou Standalone), o passo final da configuração é garantir que o app.component.html renderize nosso TaskListComponent. Abra este arquivo, apague todo o conteúdo padrão e adicione apenas a seguinte linha:

<app-task-list></app-task-list>

2. A Lógica do Componente (task-list.component.ts)

Abra src/app/components/task-list/task-list.component.ts. Aqui, vamos injetar o TarefaService, buscar os dados e definir os métodos que o HTML irá chamar.

Importante: Em uma aplicação Standalone, você deve adicionar CommonModule e FormsModule ao array imports do decorador @Component, pois eles fornecem as diretivas *ngFor e [(ngModel)].

// src/app/components/task-list/task-list.component.ts
import { Component, OnInit } from '@angular/core';
import { TarefaService } from '../../services/tarefa.service';
import { Tarefa } from '../../models/tarefa';
import { CommonModule } from '@angular/common'; // Importe para *ngFor, etc.
import { FormsModule } from '@angular/forms';   // Importe para [(ngModel)]
 
@Component({
  selector: 'app-task-list',
  standalone: true,
  imports: [
    CommonModule, // Adicione aqui
    FormsModule   // E aqui
  ],
  templateUrl: './task-list.component.html',
  styleUrl: './task-list.component.css'
})
export class TaskListComponent implements OnInit {
 
  tarefas: Tarefa[] = [];
  novaTarefa: Tarefa = { descricao: '', concluida: false };
 
  constructor(private tarefaService: TarefaService) { }
 
  ngOnInit(): void {
    this.carregarTarefas();
  }
 
  carregarTarefas(): void {
    this.tarefaService.getTarefas().subscribe(data => {
      this.tarefas = data;
    });
  }
 
  adicionarTarefa(): void {
    if (this.novaTarefa.descricao.trim() === '') return;
 
    this.tarefaService.addTarefa(this.novaTarefa).subscribe(tarefaAdicionada => {
      this.tarefas.push(tarefaAdicionada);
      this.novaTarefa = { descricao: '', concluida: false };
    });
  }
 
  atualizarStatus(tarefa: Tarefa): void {
    this.tarefaService.updateTarefa(tarefa).subscribe();
  }
 
  deletarTarefa(id: number | undefined): void {
    if (id === undefined) return;
 
    this.tarefaService.deleteTarefa(id).subscribe(() => {
      this.tarefas = this.tarefas.filter(t => t.id !== id);
    });
  }
}

3. A Aparência do Componente (task-list.component.html)

Abra src/app/components/task-list/task-list.component.html e substitua seu conteúdo pelo HTML que irá renderizar nosso formulário e a lista de tarefas.

<div class="container">
  <h1>Minha Lista de Tarefas</h1>
 
  <form class="form-add" (ngSubmit)="adicionarTarefa()">
    <input
      type="text"
      placeholder="O que precisa ser feito?"
      [(ngModel)]="novaTarefa.descricao"
      name="descricao"
      required
    >
    <button type="submit">Adicionar</button>
  </form>
 
  <ul class="task-list">
    <li *ngFor="let tarefa of tarefas">
      <input
        type="checkbox"
        [(ngModel)]="tarefa.concluida"
        (change)="atualizarStatus(tarefa)"
      >
      <span [ngClass]="{'completed': tarefa.concluida}">
        {{ tarefa.descricao }}
      </span>
      <button class="delete-btn" (click)="deletarTarefa(tarefa.id)">×</button>
    </li>
  </ul>
</div>

4. O Estilo do Componente (task-list.component.css)

Por fim, adicione um pouco de CSS em src/app/components/task-list/task-list.component.css para deixar a aplicação mais agradável.

.container {
  max-width: 600px;
  margin: 2rem auto;
  font-family: sans-serif;
  padding: 1rem;
}
 
.form-add {
  display: flex;
  margin-bottom: 1.5rem;
}
 
.form-add input {
  flex-grow: 1;
  padding: 0.8rem;
  border: 1px solid #ccc;
  border-radius: 4px 0 0 4px;
}
 
.form-add button {
  padding: 0.8rem 1.2rem;
  border: none;
  background-color: #007bff;
  color: white;
  cursor: pointer;
  border-radius: 0 4px 4px 0;
}
 
.task-list {
  list-style: none;
  padding: 0;
}
 
.task-list li {
  display: flex;
  align-items: center;
  padding: 0.8rem;
  border-bottom: 1px solid #eee;
}
 
.task-list li:last-child {
  border-bottom: none;
}
 
.task-list li span {
  flex-grow: 1;
  margin-left: 1rem;
}
 
.task-list li span.completed {
  text-decoration: line-through;
  color: #888;
}
 
.delete-btn {
  border: none;
  background: transparent;
  color: #ff4d4d;
  cursor: pointer;
  font-size: 1.2rem;
  font-weight: bold;
}

Etapa 4: Rodando a Aplicação Completa!

Chegou a hora de ver tudo funcionando junto.

  1. Garanta que sua API Spring Boot esteja rodando.
  2. Abra um terminal na pasta do projeto Angular (lista-tarefas-web) e execute:
ng serve --open

Seu navegador abrirá em http://localhost:4200 e você poderá interagir com sua aplicação de lista de tarefas!