Criando um projeto real-time com Phoenix LiveView

Vitor Roque
January 12, 2024

Neste artigo iremos falar sobre o Phoenix LiveView. Juntos, construiremos uma aplicação web bem simples, semelhante ao Twitter. Visitantes poderão postar mensagens de texto que serão exibidas em uma única página. Todos aqueles contectados à aplicação, verão as novas mensagens publicadas em real-time!

Criando o projeto

Primeiro rodamos o comando para criar o projeto liveview

mix phx.new liveview_notes --live --no-ecto

Note que utilizamos duas opções:--live e --no-ecto. A opção --live indica que também sejam criados os arquivos necessários para o LiveView; e--no-ecto evita que os arquivos Ecto sejam criados, e permite que nossa aplicação rode sem depender de uma conexão com o banco de dados.

OBS: Caso clone o projeto do repositório terá de rodar os comandos do README.md se certificando de rodar o comando npm install dentro da pasta assets.

Criando template base

Dentro do nosso projeto Phoenix, os templates HTML possuem extensão .html.eex e encontram-se na pasta lib/liveview_notes_web/templates. Dentro desta pasta, os templates HTML que servem de base para outros templates encontram-se na pasta layout. Dentro de layout, vamos editar o arquivo root.html.eex para que fique com o conteúdo a seguir:

Criando primeiro liveview

Nossas notas serão como posts no twitter, com uma regra para facilitar o entendimento da ferramenta, que é o limite de caracteres.

Em lib/liveview_notes_web/live teremos dois arquivos que, na verdade, se complementam: page_live.ex e page_live.html.leex

O page_live.ex é um módulo como outro no Elixir. Este módulo implementa um behaviour de LiveView. Isso significa que ele precisa se comportar e implementar algumas funções necessárias.

O behaviour de LiveView espera uma função render, porém, podemos usar uma forma de render através de um template: criamos um módulo e um template html.leex dentro da mesma pasta. Por exemplo: component.ex e component.html.leex.

Vamos alterar o page_live.ex para renderizar via template. Desta forma, a função mount terá o assign com algumas informações que precisamos, tais como o texto que será publicado, as notas de rascunho que estão na aplicação, as notas publicadas e um erro caso atinja o limite de caracteres.

Agora precisamos criar os eventos, que são a regra do limite de caracteres e a criação da nota. Faremos isto através do código a seguir, também em page_live.ex:

E o template page_live.html.leex ficará assim:

Em page_live.ex, criamos o evento e adicionamos um case: caso o tamanho da string seja 50 ou maior, ele preenche o assign error com a mensagem “limite atingido”.

No template page_live.html.leex, criamos o form com o phx-change (um hook direto para o evento que criamos) e o phx-submit para o evento de criar a nota.

Caso o visitante escreva mais de 50 caracteres, a mensagem de erro será exibida na tela.

Ao clicar no botão Salvar, o evento create_note é disparado e é adicionado o texto da mensagem dentro do assign draft_notes.

A seguir, o código de listagem das mensagens:

Note que o botão Publicar irá invocar o evento “publish” em nosso componente page_live.ex. Dentro do componente, iremos implementar um handler que será responsável pela propagação de novas mensagens para os outros visitantes conectados à aplicação.

Propagando as notas publicadas para os usuarios

Agora que com o LiveView foi possível fazer as coisas serem reativas, também podemos fazer um broadcast para os usuários que estão presentes na aplicação, e para isso precisamos fazer uma alteração na função mount/3.

A chamada LiveviewNotesWeb.Endpoint.subscribe(@topic) faz com que cada usuário que visite a página esteja “assinado” ao novo topic pub/sub published_notes. Este topic será usado para a propagação de novas mensagens.

Também vamos precisar criar os eventos que vão tanto publicar as notas na nossa tela como na tela dos outros usuários.

No primeiro evento nós mandamos o broadcast para todos, passando o atom e a nota que queremos publicar para os usuários. No segundo evento nós pegamos a data que foi publicada e criamos uma nota com título e data de publicação. Em seguida, retiramos a nota do assign de draft_notes pois ela já foi publicada, e adicionamos o map criado ao assign de published_notes

Com isso, por esse handle_info nós conseguimos disparar o broadcast. Os visitantes conectados à aplicação verão todas as notas publicadas — tanto aquelas criadas por eles mesmos quanto as criadas por outros usuários.

Link para o repositorio