Introdução
Aplicativos de página única (SPA) proporcionam uma melhor experiência do usuário, removendo carregamentos de página inteira, proporcionando transições suaves entre as páginas e melhorias de desempenho dependendo da escala do aplicativo, mas eles vêm com alguns custos que podem não valer a pena para alguns tipos de aplicativos. O principal custo é a complexidade adicional de um framework SPA como Angular ou Vue.
Neste post, mostrarei como usar ASP.NET e HTMX para construir aplicativos de página única sem usar JavaScript e frameworks SPA.
O que é HTMX
HTMX é uma biblioteca que nos permite acessar recursos modernos do navegador, estendendo o HTML, sem a necessidade de usar Javascript diretamente. Ele nos dá acesso a AJAX, CSS Transitions, WebSockets e muito mais, incluindo extensões.
Ele simplifica a criação de aplicativos responsivos que oferecem aos nossos usuários uma melhor experiência sem a complexidade do Javascript.
HTMX é realmente poderoso, mas neste post vou me concentrar no atributo hx-boost
.
Aplicação base
Usarei um antigo Aplicativo de exemplo do Razor Pages por Damian Edwards para mostrar como é fácil fazer um SPA com ASP.NET e HTMX.
Fiz algumas alterações, incluindo a atualização do .NET Core 2.0 para o .NET 7. Essas alterações estão no branch sample-base
do repositório de amostra: AspNetCoreSPAHtmx.
💡 Estou usando Razor pages, mas o que mostrarei também é aplicável ao ASP.NET MVC.
Incluindo a biblioteca HTMX e configurações de links de menu
Vamos abrir o _Layout.cshtml
e adicionar a referência ao HTMX no final da tag <body>
:
|
|
HTMX tem um atributo hx-boost
que nos permite transformar nosso aplicativo de várias páginas em um aplicativo de página única (SPA) com apenas alguns ajustes, fazendo com que as tags <a>
e <form>
usem solicitações AJAX em vez de recarregamentos de página.
Ele funciona em todos os filhos do elemento ao qual é aplicado, então vamos incluí-lo na tag <nav>
para que todos os links do menu usem AJAX:
<nav class="navbar navbar-inverse navbar-fixed-top" hx-boost="true" hx-target="#main-content">
Por padrão, hx-boost
aplica a resposta AJAX à tag <body>
, então precisamos especificar o id do destino (neste exemplo, #main-content
) no atributo hx-target
.
Agora, vamos criar uma div
com id main-content
para receber o conteúdo da resposta dentro da div body-content
:
|
|
ℹ️ Observe que a hora do carregamento da página inteira está no rodapé, para que possamos verificar se as solicitações são feitas com AJAX.
Removendo o layout para solicitações HTMX
Todas as solicitações feitas com HTMX terão um cabeçalho Hx-Request
definido como true
.
Vamos verificar isso no arquivo _ViewStart.cshtml
e remover o layout da página para solicitações HTMX:
|
|
Configurando os formulários
Agora, também precisamos incluir os atributos hx-boost
e hx-target
nas tags <form>
nos arquivos Index.cshtml
, New.cshtml
e Edit.cshtml
:
<form method="post" class="form-horizontal" hx-boost="true" hx-target="#main-content">
Agora podemos testar o aplicativo. Observe que a hora do carregamento da página no rodapé não muda:
Olhando para o console do navegador, podemos ver que a solicitação foi feita:
O conteúdo do formulário no payload da solicitação:
A resposta sem o conteúdo _Layout.cshtml
:
A resposta renderizada na página:
💡 A validação de formulário ASP.NET também funcionará:
Alterando o título da página de acordo com o carregamento da página
Até agora, o título da página não está sendo alterado, porque está definido no arquivo _Layout.cshtml
, que é carregado apenas no primeiro carregamento da página:
<title>@ViewData["Title"] - Razor Pages Sample + HTMX</title>
O atributo hx-boost
mudará automaticamente o título da página se a resposta retornar uma tag <title>
. Para fazer isso, vamos criar um novo layout para as solicitações HTMX.
Crie um _LayoutHtmxBoost.cshtml
com o seguinte:
|
|
Observe que retornamos uma tag <title>
se o ViewData["Title"]
tiver algum valor.
Agora, vamos alterar o arquivo _ViewStart.cshtml
para usar o layout _LayoutHtmxBoost
para solicitações HTMX:
|
|
⚠️ Estamos verificando o cabeçalho
Hx-Boosted
, solicitações HTMX não impulsionadas não usam este layout.
💡 HTMX automaticamente envia as páginas para o histórico do navegador:
Indicando o status de carregamento
Isso é legal, mas não temos um indicador de que a solicitação está sendo processada. Para isso, podemos usar o atributo hx-indicator
, especificando o id do elemento a ser mostrado enquanto aguarda a solicitação.
O hx-indicator
funciona adicionando a classe css htmx-request
ao elemento especificado, que definirá a opacidade
como 1
. Para que isso funcione, primeiro precisamos ocultar o elemento com uma opacidade
de 0
, ou usar a classe css htmx-indicator
que faz isso com uma transação css.
Primeiro, criei uma página parcial com um carregador de Pure CSS Loaders com o nome _LoadSpinner
, e defini o id
como spinner
e adicionei a classe css htmx-indicator
:
|
|
Então, adicionei no menu:
|
|
Por último, adiciono o atributo hx-indicator
com o valor #spinner
ao elemento <nav>
:
<nav class="navbar navbar-inverse navbar-fixed-top" hx-boost="true" hx-target="#main-content" hx-indicator="#spinner">
e todos os elementos <form>
:
<form method="post" class="form-horizontal" hx-boost="true" hx-target="#main-content" hx-indicator="#spinner">
💡 Para facilitar a visualização do indicador, incluí um atraso de 500 milissegundos em todos os métodos GET/POST das páginas:
1 2 3 4 5 6
public async Task<IActionResult> OnGetAsync(int id) { await Task.Delay(TimeSpan.FromMilliseconds(500)); ... }
Executando o aplicativo novamente, podemos ver o indicador mostrando em todas as ações: