Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 9, 2022 06:57 pm GMT

Uma introduo ao Hilla

Este o meu primeiro post nesta plataforma e pra marcar esta estreia, gostaria de escrever sobre um novo framework em Java desenvolvido pela empresa que trabalho: o Hilla.

O que Hilla?

Nas palavras do prprio site oficial do framework, "Hilla integra um back end em Spring Boot com um front end reativo em TypeScript".

Hilla surgiu como uma alternativa ao framework fullstack da Vaadin, o Flow. Anteriormente chamado de Fusion, decidiu-se que era o momento para uma nova cara e posicionamento da marca, para torn-la mais visvel aos desenvolvedores que antes poderiam ficar confusos em haver duas opes de desenvolvimento dentro da mesma ferramenta.

Ainda possvel que uma aplicao rode em modo hbrido (com pginas em Hilla e Flow), mas isso mais indicado para aplicaes j existentes que desejem transicionar de um modelo para o outro.

Hilla uma palavra finlandesa dado para uma fruta chamada amora-branca-silvestre, bastante comum na regio onde fica a sede da Vaadin.

Mas como isso funciona?

Em essncia, Hilla permite que o desenvolvedor crie endpoints no back end em Java que so ento usados pelo framework para gerar classes e funes em TypeScript que podem ser usados para comunicao do front end com o back end. Como esta criao dos endpoints no cliente feita de forma automtica pelo framework, o desenvolvedor tem a possibilidade de ser informado de eventuais erros de maneira mais rpida.

Alm da gerao dos endpoints, Hilla se encarrega de criar todos os tipos dos modelos criados em Java para TypeScript. Um outro ponto a se destacar que Hilla utiliza-se da biblioteca Lit para criar as views no front end. Lit uma biblioteca desenvolvida pelo Google para criao de custom components definidos pelos padres da web. A escolha de uma biblioteca no front end faz com que Hilla consiga prover algumas ferramentas de suporte na criao de aplicativos, como por exemplo, componentes, lgica para validao de formulrios, criao de rotas, entre outros.

Criando uma aplicao em Hilla

Para se criar e executar uma aplicao em Hilla, voc precisar ter instalados em seu ambiente:

  • Node 16.14 ou mais novo
  • JDK 11 ou mais novo

Para criar um novo projeto, voc pode utilizar o CLI da Vaadin rodando o comando:

npx @vaadin/cli init --hilla my-hilla-project

Entre na pasta recm-criada do projeto e inicie a aplicao executando o Maven wrapper incluso:

cd my-hilla-project./mvnw

Aps o projeto ser inicializado e o front end bundler terminar de ser executado, voc ver uma pgina igual a essa:

Projeto Hilla rodando pela primeira vez

Pronto! Agora estamos preparados para botar a mo na massa.

Estrutura do projeto

Existem duas pastas principais em um projeto Hilla, /src e /frontend e sero nelas que iremos trabalhar:

  • Na pasta /src, est contida a parte do projeto que ser executada no servidor. onde encontraremos todo o nosso cdigo em Java e tambm alguns recursos, como imagens e cones.
  • Como o prprio nome sugere, a pasta /frontend contm o cdigo que ser executado no cliente, como as views e tambm os arquivos de estilo em CSS.

Criando o primeiro endpoint

Como todo bom tutorial, a gente vai fazer um simples gerenciador de tarefas. Vamos comear criando a classe de modelo Todo em /src/main/java/com/example/models/Todo.java:

package com.example.application.models;import javax.validation.constraints.NotBlank;public class Todo {    private Integer id;    @NotBlank    private String task;    private boolean done;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getTask() {        return task;    }    public void setTask(String task) {        this.task = task;    }    public boolean isDone() {        return done;    }    public void setDone(boolean done) {        this.done = done;    }}

Agora, iremos criar a classe que abrigar o nosso primeiro endpoint TodoService em /src/main/java/com/example/services/TodoService.java:

package com.example.services;import java.util.ArrayList;import java.util.List;import com.example.models.Todo;import com.vaadin.flow.server.auth.AnonymousAllowed;import dev.hilla.Endpoint;import dev.hilla.Nonnull;@Endpoint@AnonymousAllowedpublic class TodoService {    private final List<Todo> todoList = new ArrayList<>();    public @Nonnull List<@Nonnull Todo> getTodos() {        return todoList;    }    public @NonNull Todo save(@NonNull Todo todo) {        todo.setId(todoList.size());        todoList.add(todo);        return todo;    }}

Vamos agora dissecar um pouco o cdigo que acabamos de criar.

A classe Todo um simples JavaBean com alguns setters e getters. Note que podemos adicionar alguns validadores, como @NotBlank, s propriedades da classe.

Na classe TodoService onde a maior parte da magia acontece:

  • Primeiro, anotamos a classe como @Endpoint. Isso importante para que a classe seja usada pelo framework para gerar a sua contraparte no front end em TypeScript.
  • A seguir, encontramos a anotao @AnonymousAllowed. Por padro, todos os endpoints necessitam de autenticao para serem usados. Esta anotao faz com que qualquer usurio consiga acessar aos servios dessa classe.
  • Dentro da classe, definimos os mtodos que sero expostos no cliente: primeiro um mtodo para recuperar a lista de tarefas e um outro para salvar uma nova tarefa. Note que usamos a anotao @Nonnull em getTodos e em save. Isto faz com que o gerador no use undefined como um possvel valor de retorno dos mtodos.

Aps, criarmos e salvarmos estas classes, voc ir notar (se o servidor ainda estiver rodando ou na prxima vez que ele for inicializado) que algo de novo apareceu dentro da pasta /frontend/generated:

Arquivos gerados pelo Hilla

Esses arquivos correspondem ao servio e o modelo criados anteriormente:

  • Todo.ts uma interface que contm as mesmas propriedades definidas em Todo.java
  • TodoModel.ts uma classe extendendo ObjectModel e basicamente usada para validao e vinculao das propriedades de Todo.ts com componentes de formulrio.
  • TodoService.ts define e exporta as funes correspondente aos endpoints definidos em TodoService.java

Criando a pgina de Todos

Agora que temos os endpoints criados, resta-nos criar a pgina para adicionar e visualizar as tarefas. Vamos criar um novo arquivo em /frontend/views/todo/todo-view.ts:

import { html } from 'lit';import { customElement } from 'lit/decorators.js';import { View } from '../view';@customElement('todo-view') // 1export class TodoView extends View { // 2    render() { // 3        return html` // 4            <h1>Minhas tarefas</h1>        `;    }}

Como falado anteriormente, Hilla utiliza a biblioteca Lit para criar as views no seu projeto. Vamos ver o que acabamos de criar:

  1. @customElement('todo-view') - Como mencionado, Lit uma biblioteca para criao de custom elements. Essa anotao serve para marcar a classe como um custom element ao mesmo tempo que define o nome do elemento criado (obrigatoriamente, o nome deve conter pelo menos um trao - para evitar colises com as tags padres de HTML).
  2. export class TodoView extends View - cria um classe TodoView extendendo a classe View provida pelo prprio projeto. Normalmente, estenderamos diretamente da classe LitElement, mas usando View temos algumas vantagens, como suporte a MobX, alm de desabilitar o shadow root do custom element criado.
  3. render() - mtodo invocado pelo Lit no momento em que uma instncia do componente for renderizado na pgina.
  4. return html - aqui definiremos o markup da instncia do nosso component. No momento, o componente ir adicionar um <h1> com o texto "Minhas tarefas" na tela.

Por enquanto, no podemos ver a nossa nova pgina funcionando, porque ainda no definimos nenhuma rota para ela. Para isso, basta que alteremos o arquivo em /frontend/routes.ts e adicionar uma nova entrada no array views:

import './views/todo/todo-view';// ...export const views: ViewRoute[] = [  // ...    },  {    path: 'todo',    component: 'todo-view',    title: 'Tarefas',  },];

E, pronto! Temos a nossa pgina funcionando...

Nova pgina "Minhas tarefas" funcionando!

... bem, mais ou menos. Ainda no temos como adicionar as nossas tarefas .

Dando vida nossa lista de tarefas

Agora podemos adicionar nosso pequeno formulrio e tabela para adicionar e visualizar as nossas tarefas. Os componentes que iremos utilizar fazem parte do pacote do design system criado pela Vaadin e j utilizados por milhares de programadores em todo mundo. Vamos voltar ao nosso arquivo e inserir as funcionalidades restantes:

Primeiro, adicionaremos duas propriedades class TodoView:

  @state()  private todos: Todo[] = []; // 1  private binder = new Binder(this, TodoModel); // 2
  1. Um array com o nome todos que servir para armazenar a lista de tarefas adicionadas pelo usurio. Note a marcao @state, que serve para fazer o Lit observar e reagir s alteraes nesta propriedade. Alm disso, marcamos a propriedade com o tipo gerado Todo.
  2. Instncia de Binder (uma classe para manipulao de campos de formulrio) que usaremos no formulrio de criao de uma nova tarefa.

Com isso feito, vamos agora alterar o mtodo render e adicionar o restante dos elementos da nossa pgina:

  render() {    return html`      <section class="p-m">        <h1>Minhas tarefas</h1>        <div theme="spacing" class="flex gap-s items-end">          <vaadin-text-field            label="Nova tarefa"            ${field(this.binder.model.task)}            placeholder="Comprar ovos, estudar hilla..."            style="width: 300px;"          ></vaadin-text-field> <!-- 1 -->          <vaadin-button @click="${this.createTodo}" theme="primary">Adicionar</vaadin-button> <!-- 2 -->        </div>        <div class="todos">          ${this.todos.length === 0            ? html` <span>Nenhuma tarefa adicionada</span> `            : this.todos.map(                (todo) => html`                  <div class="todo ${todo.done ? 'done' : ''}">                    <vaadin-checkbox                      ?checked="${todo.done}"                      @checked-changed="${(e: CheckboxCheckedChangedEvent) => this.updateTodo(todo, e.detail.value)}"                    ></vaadin-checkbox>  <!-- 4 -->                    <span>${todo.task}</span>                  </div>                `              )}  <!-- 3 -->        </div>      </section>    `;  }
  1. Criamos o campo de texto com um placeholder e usamos a diretiva field para atrelar a propriedade task de Todo ao valor inserido pelo usurio no campo.
  2. Um boto, com um ouvinte de eventos (event listener) para o evento de clique do usurio. Adiante iremos mostrar a implementao do mtodo createTodo.
  3. A lista de tarefas criadas. Primeiro verificamos se a lista de tarefas est vazia para apresentar uma mensagem e, em caso contrrio, iteramos sobre a lista todos atravs do mtodo map para retornar uma lista de tarefas. Para quem tem experincia com React, poder ver uma certa semelhana aqui.
  4. Um campo checkbox para que o usurio marque/desmarque a tarefa como feita. Adicionamos um event listener que ser chamado toda vez que o usurio alterar o valor do campo.

Agora, precisamos chamar o mtodo no servidor que retorna a lista de tarefas criadas. Para isso, podemos usar o mtodo de tempo de vida (lifecycle) connectedCallback. Este mtodo chamado toda vez que o componente adicionado pgina:

  async connectedCallback() {    super.connectedCallback();    this.todos = await getTodos(); // 1  }
  1. Como estamos usando await aqui, precisamos marcar o mtodo com async. getTodos() a funo gerada no cliente que far a chamada ao servidor do mtodo TodoService#getTodos que criamos anteriormente.

Por ltimo, adicionaremos os mtodos referenciados no markup criado:

  async createTodo() {    const todo = await this.binder.submitTo(save); // 1    if (todo) {      this.todos = [...this.todos, todo]; // 2       this.binder.clear(); // 3    }  }  updateTodo(todo: Todo, done: boolean) {    const updatedTodo = { ...todo, done };    this.todos = this.todos.map((t) => (t.id === todo.id ? updatedTodo : t)); // 4    save(updatedTodo); // 5  }
  1. Chamamos o mtodo submitTo de binder que recebe um callback como parmetro. Este mtodo passar uma instncia de Todo com o valor do campo de texto inserido pelo usurio na propriedade task.
  2. Alteramos o objeto this.todos para adicionar a nova tarefa retornada em (1).
  3. O mtodo clear de binder limpa os campos controlados por ele atravs da diretiva field.
  4. Atualizamos o objeto this.todos como a tarefa com o seu novo status (feito/no feito).
  5. Por fim, chamamos a funo save para persistir a alterao da tarefa no servidor.

Abaixo o arquivo todo-view.ts completo:

todo-view.ts

import { html } from 'lit';import { customElement, state } from 'lit/decorators.js';import { View } from '../view';import '@vaadin/text-field';import '@vaadin/button';import '@vaadin/checkbox';import { Binder, field } from '@hilla/form';import { CheckboxCheckedChangedEvent } from '@vaadin/checkbox';import { getTodos, save } from 'Frontend/generated/TodoService';import Todo from 'Frontend/generated/com/example/application/models/Todo';import TodoModel from 'Frontend/generated/com/example/application/models/TodoModel';@customElement('todo-view')export class TodoView extends View {  @state()  private todos: Todo[] = [];  private binder = new Binder(this, TodoModel);  async connectedCallback() {    super.connectedCallback();    this.todos = await getTodos();  }  render() {    return html`      <section class="p-m">        <h1>Minhas tarefas</h1>        <div theme="spacing" class="flex gap-s items-end">          <vaadin-text-field            label="Nova tarefa"            ${field(this.binder.model.task)}            placeholder="Comprar ovos, estudar hilla..."            style="width: 300px;"          ></vaadin-text-field>          <vaadin-button @click="${this.createTodo}" theme="primary">Adicionar</vaadin-button>        </div>        <div class="todos">          ${this.todos.length === 0            ? html` <span>Nenhuma tarefa adicionada</span> `            : this.todos.map(                (todo) => html`                  <div class="todo ${todo.done ? 'done' : ''}">                    <vaadin-checkbox                      ?checked="${todo.done}"                      @checked-changed="${(e: CheckboxCheckedChangedEvent) => this.updateTodo(todo, e.detail.value)}"                    ></vaadin-checkbox>                    <span>${todo.task}</span>                  </div>                `              )}        </div>      </section>    `;  }  async createTodo() {    const todo = await this.binder.submitTo(save);    if (todo) {      this.todos = [...this.todos, todo];      this.binder.clear();    }  }  updateTodo(todo: Todo, done: boolean) {    const updatedTodo = { ...todo, done };    this.todos = this.todos.map((t) => (t.id === todo.id ? updatedTodo : t));    save(updatedTodo);  }}

... e pronto! Temos o nosso gerenciador de tarefas em perfeito funcionamento:

Lista de tarefas criadas

Consideraes finais

O intuito deste artigo foi mostrar de uma forma simples o bsico para se comear a criar uma aplicao em Hilla. H muito mais a ser explorado que precisou ser deixado de fora deste artigo porque ele ficaria imenso e cansativo.

A pgina de documentao bastante extensa e apresenta as demais funcionalidades do framework que o permitem ser uma escolha para diversos tipos de projetos. Infelizmente, s temos a verso dela em ingls.

Espero que a leitura tenha valido a pena e, por favor, qualquer feedback mais que bem-vindo!

At mais!


Original Link: https://dev.to/diegocardoso/uma-introducao-ao-hilla-4m83

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To