DEV Community

Cover image for Django, Htmx e React: usando HTMX para além de TODO-Lists
Eduardo Oliveira
Eduardo Oliveira

Posted on

Django, Htmx e React: usando HTMX para além de TODO-Lists

Já faz algum tempo que o HTMX, que me foi apresentado pelo @JeffQuesado, me pareceu uma ideia muito legal para se trabalhar junto com Django, principalmente o Django-Admin que já tem uma estrutura fortemente baseada em server-side.

Conteúdos

Introdução

Em uma demanda de um dos projetos em que atuo, precisávamos de uma funcionalidade para a qual o uso do HTMX se encaixaria perfeitamente e esse texto tem como objetivo levantar questões pelas quais foi decidido o uso do HTMX e não de outras ferramentas e elucidar como foram feitas integrações entre Django, HTMX e React para construir ferramentas reais.

O título é uma inspiração de um tweet que vi, alguns dias atrás, que questionava se alguém estava utilizando o HTMX para algo mais além de criar TODO-Lists.

Contextualização da Plataforma Atual

O back-end do projeto utiliza o Django com Django-Rest-Framework para servir uma API de integração para um app mobile e, para gerenciamento dos conteúdos, o Django-Admin é utilizado.

O django-admin possuí, nas páginas de formulário, uma estrutura parecida com a seguinte: um cabeçalho ocupado toda a largura da página, e logo a baixo uma sidebar e o conteúdo principal, que no caso é um formulário.

Cabeçalho ocupando toda a largura da página e logo abaixo uma sidebar e o conteúdo principal, que no caso é um formulário

Em alguns formulários, a estrutura e arquitetura de informação dos models demandava um controle de carregamento e de exibição de itens complexo usando lazy-loading. Desse modo, quando o ferramental do django e do django-admin deixou de suprir as necessidades, parte desses formulários foi construída utilizando aplicações pequenas com React.

Essas aplicações React que eram carregadas apenas em páginas específicas não demandavam, por exemplo, o uso de routers e coisas do tipo.

Entendendo o Problema

O painel de administração é utilizado para algo próximo a um CMS (content management system) para um app de idiomas. Durante a criação de algumas "entidades" dentro da plataforma, seria necessário uma ferramenta, em popup, para consultar as "entidades" já cadastradas, pois esse processo atualmente era gerenciado com planilhas externas, aumentando a complexidade no uso do painel.

Essa mesma ferramenta deveria, também, fornecer a opção de clonar entidades para a página em que se estava trabalhando (alternado a entidade paí/chave estrangeira).

Por que HTMX se já usávamos React?

Quando começamos a avaliar a funcionalidade, a primeira discussão foi: será que o HTMX se encaixa bem aqui? ou será melhor lidar com React?

Um dos principais pontos levados em consideração: a estrutura do django e do django-admin já trazia uma quantidade enorme de código e funcionalidades que poderiam ser reutilizadas para a exibição, como paginação, etc. na aplicação react que roda nessa aplicação não lidava com paginação nem com diversas outras coisas que demandaria muito mais tempo para implementar que utilizando partes dos templates do django-admin adaptados.

Como essa funcionalidade não seria, sempre, inserida nas mesmas páginas que o formulário react, haveria muito trabalho pra conciliar o uso do react, bundlers, etc.

Esse conjunto de fatores foram os principais motivantes para a adoção do HTMX, que diferente do react que teriamos que lidar com bundler, só importamos o script onde fosse necessário utilizar as tags do HTMX.

A simplicidade que o HTMX trouxe para essa implementação, talvez tivesse sido utilizada no lugar do react se a conhecêssemos na época.

Questões técnicas

Algumas questões que foram foco dos esforços no trabalho com essa funcionalidade serão tratadas nessa seção. Essas questões não foram, necessariamente, complexas ou problemáticas. São apenas pontos aos quais gostaria de comentar.

Comunicação entre HTMX e React

Como comentado, em algum lugar no texto acima, era necessário clonar itens para o formulário atual. Formulário esse, construído em React. Assim, ao integrar uma ação para clonar o item, utilizando o hx-post, é necessário que algum trigger seja disparado para que alguma parte da app React possa processar.

Um detalhe, ao qual não vou adentrar aqui, é que esses "links" para ações estão dentro de um formulário, então na chamada para o hx-post todos os campos do formulário também são enviados.

<span
  hx-post="/admin/my-action"
  hx-trigger="confirmed"
  hx-indicator=".htmx-indicator"
  title="Clonar"
>
  ...
</span>
Enter fullscreen mode Exit fullscreen mode

O hx-trigger="confirmed" se deve a seção abaixo, onde tratamos sobre a confirmação para a execução da ação. Fora isso, no lado do servidor, utilizando django-htmx podemos inserir uma header HX-Trigger para disparar um trigger quando a ação do HTMX for finalizada:

def my_view(request):
    ...
    response = render(request, 'my_template.html', my_context)
    resp.headers['HX-Trigger'] = 'completeClone'
    return resp
Enter fullscreen mode Exit fullscreen mode

Dentro do cliente react podemos adicionar o evento ao document devido ao event bubbling:

useEffect(() => {
  const fetch = async () => {
    ...
  }

  document.addEventListener('completeClone', fetch);
  return () => {
    document.removeEventListener('completeClone', fetch);
  }
}, []);
Enter fullscreen mode Exit fullscreen mode

Confirmação para execução de ações

Para exibir confirmações de execução das ações, utilizamos a biblioteca SweetAlert2, que já estava no projeto pra ser utilizada em alguns outros momentos:

function confirmClone(element, title) {
  text = 'Deseja clonar o item <strong>"' + title + '"</strong>? Essa ação não poderá ser desfeita!'
  Swal
    .fire({
      title: 'Tem certeza?',
      html: text,
      showCancelButton: true,
      confirmButtonText: 'Sim, Clonar',
      cancelButtonText: 'Cancelar',
    })
    .then(function(result){
      if(!result.isConfirmed){
        return;
      }
      htmx.trigger(element, 'confirmed');
    });
}
Enter fullscreen mode Exit fullscreen mode

A linha htmx.trigger(element, 'confirmed'); dispara o trigger que está no hx-trigger="confirmed" do span. Aqui é preciso completar, também, a tag:

<span
  hx-post="/admin/my-action"
  hx-trigger="confirmed"
  hx-indicator=".htmx-indicator"
  onclick="confirmClone(this, 'título do item!!!!')"
  title="Clonar"
>
  ...
</span>
Enter fullscreen mode Exit fullscreen mode

Como é que isso ai funciona?

A implementação com HTMX fluiu muito bem, mas em diversos momentos foi necessária muita experimentação por falta de clareza do que, exatamente, os comandos fazem ou como lidar para que eles fizessem exatamente o que queria.

Mas, esse pode ser uma skill issue do prazo e de ser a primeira vez utilizando a documentação, tanto que as duas seções acima foram fortemente baseadas pelos seus exemplos (mas não pela sua documentação).

E o futuro?

Bom, desenvolver uma funcionalidade real com HTMX foi uma experiência muito boa e creio que o projeto tem muito potêncial, apesar que em diversos momentos, a depender da estrutura e do tamanho do projeto, eu continue achando que uma API (Rest ou intent based) com um cliente front-end ainda seja mais simples de se manipular.

Ainda assim, espero ter mais chances de usar o HTMX integrado ao django-admin que é, fundamentalmente, server-side.

Top comments (2)

Collapse
 
leandronsp profile image
Leandro Proença

excelente artigo Eduardo! nunca meti a mão em HTMX, parece bacana

Collapse
 
eduardojm profile image
Eduardo Oliveira

Obrigadoooo! Foi minha primeira experiência com ele, gostei bastante