Capítulo 5.6: Exibição de Dados via Blade + Fetch
Tempo de estudo: 50 minutos
1. Abordagem Híbrida: O Melhor de Dois Mundos
Podemos construir uma página de duas maneiras:
- Renderização Completa no Servidor (SSR): Laravel gera todo o HTML, incluindo a lista de planetas. Para qualquer atualização (exclusão, adição), a página é completamente recarregada.
- Renderização Completa no Cliente (CSR): Laravel entrega um "esqueleto" HTML vazio, e o JavaScript solicita todos os dados da API e os renderiza no cliente. (Esta é a abordagem Single Page Application - SPA).
Nossa escolha — a abordagem híbrida:
- Primeira carga (SSR): Laravel entrega imediatamente a página com a lista de planetas já pronta. Isso é rápido e bom para SEO. O usuário vê o conteúdo imediatamente.
- Ações subsequentes (CSR): O JavaScript intercepta as ações do usuário (cliques de botões) e interage com a API, atualizando apenas as partes necessárias da página, sem recarga completa.
💡 Analogia Espacial:
Ao entrar na ponte de comando, você recebe imediatamente o mapa de navegação principal, impresso no Centro de Controle da Missão (SSR). Ele já está em suas mãos, não há necessidade de esperar. Mas então você ativa o "modo ao vivo" em seu tablet (CSR), e ele começa a receber atualizações dos satélites em tempo real, redesenhando os objetos em seu mapa.
2. Passo 1: Preparação da Página
Trabalharemos com nossa página de lista de planetas resources/views/planets/index.blade.php
. Ela já é capaz de exibir dados passados do controlador. Agora adicionaremos a ela elementos de controle que funcionarão via JS.
Adicione um botão "Atualizar lista" e um contêiner para notificações:
<div class="controls">
<h2>Lista de todos os planetas conhecidos</h2>
<button id="refresh-btn">Atualizar via API</button>
</div>
<div id="notification-area" class="notification"></div>
<hr>
{{-- Esta div será nosso contêiner para atualização dinâmica --}}
<div id="planet-list-container" class="planet-list">
{{-- Inclui a view "filha" que renderiza a lista inicial --}}
@include('planets.partials.list', ['planets' => $planets])
</div>
Observe @include('planets.partials.list', ...)
. Extraímos a lógica de exibição da lista para um arquivo separado e reutilizável.
Passo 2: Criação de uma View "Parcial" Reutilizável (Partial)
Extrair partes repetidas para arquivos separados é uma boa prática.
Crie o arquivo resources/views/planets/partials/list.blade.php
:
@forelse($planets as $planet)
<div class="planet-card" id="planet-card-{{ $planet->id }}">
<h3>{{ $planet->name }}</h3>
<p>Sistema Solar: {{ $planet->solar_system }}</p>
<p>Diâmetro: {{ number_format($planet->size_km, 0, '.', ' ') }} km</p>
<a href="{{ route('planets.show', $planet) }}">Saber mais →</a>
<button class="delete-btn" data-id="{{ $planet->id }}" data-url="{{ route('api.planets.destroy', $planet) }}">
Desativar
</button>
</div>
@empty
<p>Não há nenhum planeta no banco de dados.</p>
@endforelse
- Importante: Observe que a URL para o botão de exclusão agora é gerada para a rota da API:
route('api.planets.destroy', $planet)
. Para isso, certifique-se de que emroutes/api.php
você tenha um recurso nomeado:Route::apiResource('planets', ...)->name('api.planets');
Passo 3: Escrita de JavaScript para Atualização Dinâmica
Agora o mais interessante. Criaremos um JavaScript que, ao clicar em um botão, solicitará uma lista atualizada de planetas da API e a redesenhará.
Crie o arquivo public/js/planet-manager.js
e inclua-o em layouts/app.blade.php
.
document.addEventListener('DOMContentLoaded', () => {
const refreshBtn = document.getElementById('refresh-btn');
const planetListContainer = document.getElementById('planet-list-container');
const notificationArea = document.getElementById('notification-area');
// Função para exibir notificações
function showNotification(message, isError = false) {
notificationArea.textContent = message;
notificationArea.className = isError ? 'notification error' : 'notification success';
setTimeout(() => {
notificationArea.textContent = '';
notificationArea.className = 'notification';
}, 3000);
}
// Função para renderizar um único cartão de planeta
function createPlanetCardHtml(planet) {
// IMPORTANTE: Geramos o mesmo HTML que em nossa view parcial
return `
<div class="planet-card" id="planet-card-${planet.id}">
<h3>${planet.name}</h3>
<p>Sistema Solar: ${planet.solar_system}</p>
<p>Diâmetro: ${new Intl.NumberFormat().format(planet.size_km)} km</p>
<a href="/planets/${planet.id}">Saber mais →</a>
<button class="delete-btn" data-id="${planet.id}" data-url="/api/planets/${planet.id}">
Desativar (JS)
</button>
</div>
`;
}
// Função para solicitar e redesenhar a lista de planetas
async function fetchAndRenderPlanets() {
showNotification('Solicitando novos dados de satélites orbitais...');
try {
const response = await fetch('/api/planets', {
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error('Erro de rede ao obter dados.');
}
const planets = await response.json(); // Laravel por padrão retornará { data: [...] } para um recurso paginado
planetListContainer.innerHTML = ''; // Limpa a lista antiga
if (planets.data.length === 0) {
planetListContainer.innerHTML = '<p>Não há nenhum planeta no banco de dados.</p>';
} else {
planets.data.forEach(planet => {
const cardHtml = createPlanetCardHtml(planet);
planetListContainer.innerHTML += cardHtml;
});
}
showNotification('Dados atualizados com sucesso!', false);
} catch (error) {
console.error('Erro ao atualizar a lista de planetas:', error);
showNotification(error.message, true);
}
}
// Adiciona o manipulador de eventos ao botão
if (refreshBtn) {
refreshBtn.addEventListener('click', fetchAndRenderPlanets);
}
// A lógica de exclusão do capítulo anterior pode ser movida para cá,
// para que todo o JS esteja em um só lugar.
});
3. Verificação Final
- Inicie o servidor (
php artisan serve
ou certifique-se de que o Herd esteja funcionando). - Recrie o banco de dados, se necessário:
php artisan migrate:fresh --seed
. - Abra a página
/planets
no navegador.- Você deve ver imediatamente a lista de planetas, gerada pelo servidor.
- Clique no botão "Atualizar via API".
- Você verá uma notificação de carregamento.
- A lista deve desaparecer por um momento e reaparecer, mas desta vez será gerada por JavaScript com base nos dados obtidos da API.
Você implementou com sucesso o modelo híbrido!
Quiz para Fixação
🚀 Parabéns pela conclusão do Capítulo 5!
Você percorreu um longo caminho, desde os fundamentos do Blade até a criação de páginas híbridas interativas. Você aprendeu a:
- Criar e usar templates e layouts Blade.
- Organizar rotas da web e controladores para operações CRUD.
- Proteger formulários da web e requisições AJAX com tokens CSRF.
- Integrar JavaScript para interação dinâmica com a API sem recarregar a página.
Seu Centro de Controle de Voo está totalmente funcional, seguro e interativo. Você está pronto para a próxima grande etapa — comparar esta abordagem com outros frameworks e aprender as melhores práticas para produção.