
Bem vindo programador(a)!
Nesse primeiro artigo que resolvi me aventurar em NodeJS, vou falar pra você um pouco sobre o AdonisJS, um framework MVC que utiliza Convenção sobre Configuração (CoC – Convention over Configuration) para facilitar a nossa vida :]
E pra quem não sabe, Convenção sobre Configuração é uma técnica que facilita o desenvolvimento ao diminuir o número de decisões triviais que precisamos tomar através do uso de configurações padrões, deixando apenas as decisões importantes para nós.
Vamos entender melhor no decorrer do código.
Ferramentas que utilizaremos
- AdonisJS 4.1
- SQLite
- Yarn
- Bulma
Resumo do projeto
Neste artigo vamos desenvolver um APP de eventos onde é possível que um usuário cadastre um determinado evento público (com título, data e descrição) e que outros usuários marquem se vão ao evento permitindo uma contagem de quantidade de pessoas.
Então teremos as funcionalidades:
- Cadastrar um evento
- Editar um evento
- Remover um evento
- Marcar “Eu vou”
Então prepara o relógio, pegue o café e bora!
Começando a codar!
1. Instalando o AdonisJS
Neste artigo vamos utilizar o Yarn (você pode usar o npm se desejar), então pra quem não tem o Yarn, só ir neste link das docs e instalar:
https://yarnpkg.com/en/docs/install
A – Primeiro, vamos executar o seguite comando do Yarn para instalar o AdonisJS:
1 |
sudo yarn global add @adonisjs/cli |
Estamos executando o comando com o sudo porque estamos instalando a CLI do Adonis globalmente
B – Caso queira verificar a versão
1 |
adonis --version |
Caso opter por ver a versão, ela vai aparecer a versão o Adonis CLI. A versão do AdonisJS veremos assim que criamos o projeto
2. App de eventos
Nesta parte vamos ao coração do post, onde desenvolveremos nosso APP de eventos. Repare que todos as partes a seguir onde tivermos códigos, eu colocarei o nome da branch do repositório para que você possa ver o que eu fiz passo a passo no código :]
2.1 Iniciando o projeto
A – Para iniciar o projeto, vamos executar o seguinte comando:
1 |
adonis new public_events --yarn |
Estamos iniciando um novo projeto com o nome public_events e passando a opção –yarn para utilizarmos o Yarn como gerenciador de pacotes padrão deste projeto
B – Agora vamos entrar no diretório da aplicação:
1 |
cd public_events |
C – E vamos executar o seguinte comando para iniciar o servidor de desenvolvimento do Adonis:
1 |
adonis serve --dev |
Repare que, por padrão, o Adonis inicia o servidor na porta 3333
D – Depois de iniciar o servidor, abra o seu navegador na url http://127.0.0.1:3333 para ver a página de boas vindas do Adonis, que será como esta

2.2 Diretórios do Adonis
Vamos explicar brevemente os diretórios que o Adonis tem:
- /app
Onde estão as estruturas do nosso app, como os Middlewares, Controllers e Models. Mais pra frente veremos cada um deles : )
Perceba que inicialmente há somente Middlewares e Models. Conforme formos criando as estruturas, as outras pastas vão surgindo.
- /config
Onde armazenamos as configurações da aplicação. Por exemplo, o arquivo database.js vem com as configurações de conexão ao banco de dados conforme o tipo de banco que utilizaremos.
- /database/migrations
Nesta pasta ficarão as migrations, que são estrutura que fazem operações de DDL.
- public
Aqui armazenaremos os nosso assets como CSS, JS do frontend e imagens
- resources
Aqui armazenaremos os recursos de telas como views e layouts
- start
Aqui estão os arquivos que basicamente registrarão os middlewares que nosso app utiliza e o arquivo de rotas, onde vamos definir as rotas que nosso APP terá juntamente com o controller para o qual será direcionado.
2.3 Fazendo a primeira view
[Branch: first_view]
Como dissemos anteriormente, vamos focar apenas no Adonis neste post, então não vamos utilizar empacotadores como webpack ou bower. Utilizaremos o CDN do Bulma e colocaremos todos os nossos assets direto na pasta public, que justamente onde o Adonis busca pelos assets.
Para o frontend, o Adonis utiliza uma ferramenta de template chamada Edge, Vamos entender um pouco como ele funciona nos códigos.
A – Primeiro, vamos criar um diretório resources/layouts
e dentro dele o arquivo main.edge
1 2 |
mkdir resources/views/layouts touch resources/views/layouts/main.edge |
B – E acrescente o seguinte conteúdo em resources/views/layouts/main.edge
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Public Events</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css"> <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script> </head> <body> <section class="section"> <div class="container"> @!section('content') </div> </section> </body> </html> |
O que estamos fazendo aqui é basicamente criando um template que utilizaremos em todas as nossas páginas e neste template importando o Bulma e o FontAwesome via CDN
Repare no conteúdo @!section(‘content’), ele é uma notação do Edge. É através dessa notação que vamos conseguir embutir os códigos das nossas views dentro desse template. Não se preocupe, já chegaremos lá
C – Agora vamos criar o diretório resources/views/home
e dentro dele o arquivo resources/views/home/index.edge
1 2 |
mkdir resources/views/home touch resources/views/home/index.edge |
D – E adicione o seguinte conteúdo em resources/views/home/index.edge
1 2 3 4 |
@layout('layouts.main') @section('content') <h1>Minha primeira View! = )</h1> @endsection |
Olha que interessante: primeiro chamamos o @layout('layouts.main')
justamente para informar que esta página home/index.edge
deve usar o layout que criamos anteriormente; depois, criamos um bloco com @section('content')
e @endsession
que é um recurso que vai colocar tudo que estiver dentro deste bloco que tem nome content na declaração @!section('content')
que fizemos lá em layouts/main.edge
E – Agora, por ultimo, vamos adicionar uma rota pra essa view. Vamos substituir a seguinte linha em start/routes.js
1 |
Route.on('/').render('welcome') |
Por:
1 |
Route.on('/').render('home.index') |
Estamos informando que, na rota / (rota raiz) ele deve renderizar a view home/index.edge
F – Se formos até o navegador, teremos:

2.4 Primeiro controller
[Branch: first_controller]
A – Para começar, vamos executar o seguinte comando para criarmos um controller chamado EventsController
1 |
adonis make:controller event |
B – Na execução deste comando, ele vai mostrar uma lista para escolhermos tipo de controller queremos criar. A lista vai aparecer deste jeito:
1 2 3 |
> Select controller type (Use arrow keys) For HTTP requests For Websocket channel |
Vamos selecionar a opção For HTTP requests
O Adonis nos proporciona uma CLI para que possamos criar um controller já num padrão de diretórios e mais legal ainda, ele nos pergunta que tipo que controller queremos.
Isso se deve ao fato do Adonis também ter suporte para conexões Websocket. Pra quem nunca ouviu falar, é um protocolo que roda em cima HTTP que permite que a troca de mensagens entre cliente e servidor seja feita sem a necessidade de requisição.
Por hora, já que estamos falando do básico do Adonis, vamos ficar no HTTP ^^
Repare que foi criado um diretório app/Controllers/Http
com um arquivo EventController.js
. Vamos trabalhar nele agora
C – Acrescente o seguinte código em app/Controllers/Http/EventController.js
1 2 3 4 5 6 7 |
'use strict' class EventController { index(ctx) { return ctx.view.render('event.index') } } module.exports = EventController |
Repare que adicionamos a primeira action do controller chamada index. Toda action recebe um parâmetro que contém as propriedados que nos auxiliam com a requisição e a resposta.
Observe que chamamos a propriodade view deste objeto que utitlizamos para renderizar uma view event.index
. Ao ler isso, ele vai buscar pelo aquido event/index.edge
dentro de resouces/views
(já vamos criar)
D – Vamos aproveitar a desconstrução de objetos do Javascript e refatorar esse controller
1 2 3 4 5 6 7 |
'use strict' class EventController { index({ view }) { return view.render('event.index') } } module.exports = EventController |
Utilizando a descontrução de objetos, pegamos apenas a propriodade view do antigo objeto ctx, que é a que nos interessa neste momento
E – Agora vamos criar uma view pra este controller
1 |
adonis make:view event/index --layout=layouts.main |
Utilizamos o comando do Adonis para criar a view event/index.edge
e passamos uma opção --layout
para que já acrescente a marcação @layout
pra gente
F – Deixe a view resources/views/event/index.edge
com o seguinte conteúdo
1 2 3 4 |
@layout('layouts.main') @section('content') <h1>Meus eventos</h1> @endsection |
G – Adicione a seguinte rota em start/routes.js
1 2 |
... Route.get('/events', 'EventController.index') |
Nesta rota estamos definindo a chamada GET a /events
deve ser direcionada para o controller EventController
na action index
H – Se abrirmos o navegador e formos até a url /events
veremos a view que criamos.
2.5 Customizando o layout main e adicionando uma rota nomeada
[Branch: customize_main_layout]
Nesta parte vamos apenas adicionar um estilo ao nosso layout main com o Bulma e uma rota nomeada. Aqui vou evitar entrar em detalhes sobre o CSS do Bulma pra que a gente possa focar no aprendizado do Adonis.
A – No arquivo de rotas em start/routes.js
substitua a linha abaixo
1 |
Route.get('/events', 'EventController.index') |
Por
1 |
Route.get('/events', 'EventController.index').as('events.index') |
Perceba que apenas adicionamos o método as
. Com ele, nós conseguimos dar um nome único pra nossa rota. Já vamos ver pra que serve : )
B – No layout main em resources/views/layouts/main.edge
, substitua o conteúdo atual por:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Public Events</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css"> <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script> </head> <body> <nav class="navbar has-background-grey-dark" role="navigation" aria-label="main navigation"> <div class="navbar-brand has-background-primary"> <a class="navbar-item has-text-grey-darker">Public Events</a> <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample"> <span aria-hidden="true"></span> <span aria-hidden="true"></span> <span aria-hidden="true"></span> </a> </div> <div id="navbarBasicExample" class="navbar-menu"> <div class="navbar-start"> <a class="navbar-item has-background-grey-dark has-text-light" href="/">Home</a> <a class="navbar-item has-background-grey-dark has-text-light" href="{{ route('events.index') }}">Meus Eventos</a> </div> </div> </nav> <section class="section"> <div class="container"> @!section('content') </div> </section> </body> </html> |
Adicionamos um menu superior com links para Home e Meus eventos e uma logo pro nosso app
Se atente para a linha 26 no main.edge
que acabamos de atualizar:
1 |
<a class="navbar-item has-background-grey-dark has-text-light" href="{{ route('events.index') }}">Meus Eventos</a> |
Perceba que a propriedade href possui {{ route('events.index') }}
.
As chaves duplas é o que o Edge utiliza para processar algum código no momento da renderização da view. O método route é um helper que retorna uma URL com base no parâmetro que passamos e perceba que passamos como parâmetro o mesmo valor que definimos no método as
nas rotas.
D – Se formos no navegador novamente, veremos que agora temos um menu e conseguimos navegar entre as páginas Home e Meus eventos
2.6 Criando um Model
[Branch: model_database]
Nesta parte vamos criar nosso primeiro Model, o Event. É com ele que vamos fazer as operações nos bancos de dados (caso queira pesquisar na documentação do Adonis, o model é possivel graças a uma ferramenta chamada Lucid.)
Antes de criamos o model, vamos instalar um plugin do SQLite3 para que o Adonis possa utilizar na conexão com o banco
A – Execute o comando
1 |
adonis install sqlite3 |
Com este comando ele já instala o plugin e configura
A configuração para a utilização do SQLite3 está no arquivo config/database.js
na linha
1 |
connection: Env.get('DB_CONNECTION', 'sqlite'), |
Esta variável de ambiente DB_CONNECTION
está dentro no nosso arquivo .env
, na raiz do projeto, que o Adonis já carrega por padrão.
B – Com o banco configurado, vamos criar o model Event com o comando:
1 |
adonis make:model Event |
Repare que ele criou o arquivo app/Models/Event
. Dentro deste diretório existem mais dois models, o User
e o Token
, mas podem ignorar por hora, falaremos deles mais tarde
Todo model Lucid se relaciona com uma tabela do banco de dados, então precisamos também criar uma migration pra isso. Uma migration é um trecho de código que realiza operações no banco, neste caso, vamos crirar uma tabela chamada events.
C – Para criar a migration, execute:
1 |
adonis make:migration events |
D – Ao digitar o comando, apareceção estas opções:
1 2 3 |
> Choose an action (Use arrow keys) Create table Select table |
Vamos selecionar a opção Create table, já que queremos criar a tabela events
E – Perceba que foi criado o arquivo database/migrations/<timestamp>_events_schema.js
. Este é o arquivo será executado para a criação de uma tabela no banco. Substitua o conteúdo deste arquivo por:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
'use strict' /** @type {import('@adonisjs/lucid/src/Schema')} */ const Schema = use('Schema') class EventsSchema extends Schema { up () { this.create('events', (table) => { table.increments() table.string('title') table.datetime('date') table.string('description') table.timestamps() }) } down () { this.drop('users') } } module.exports = EventsSchema |
Perceba que ele tem um método up e um down. O up é executado quando informamos que a migration deve ser processada e o down quando ela deve ser regredida. No up, chamamos uma função create que recebe o nome da tabela e uma arrow function onde colocaremos os campos que queremos na tabela, no caso nas linhas 10, 11 e 12 criaremos os campos title, date e description como string, datetime e string.
A função increments cria uma chave primária incremental e a timestamps cria um campo created_at para a data de criação e updated_at para a ultima atualização.
No método down a tabela será apagada.
F – Agora vamose rodar esta migration executando:
1 |
adonis migration:run |
Executa todas as migrations pendentes
Uma observação final: perceba que criamos o model Event e a tabela events no banco e que não fizemos nenhuma configuração pra isso. O que ocorre é que o Lucid opera com uma técnica chamada Convention Over Configuration, ou seja, já assume algumas convenções padrões para que não perdermos tempo com configurações básicas. No caso o Model, já é feito a busca de uma tabela que tenha o mesmo nome do model, mas no plural. Ou seja, o model Event já mapeia a tabela events do banco.
2.7 Formulário de criação do Evento
[Branch: create_event]
Para o formulário, utilizaremos um recurso do próprio Adonis.
A – Primeiro, vamos criar uma rota para a página create e uma outra rota que uilizaremos para processar o formulário de criação. Adicione em start/routes.js
1 2 3 |
... Route.get('/events/create', 'EventController.create').as('events.create') Route.post('/events', 'EventController.store').as('events.store') |
Criamos uma rota GET /events/create
que vai chamar a action create
no controller EventController
e carregar a view event/create
que criaremos mais pra frente e uma outra rota POST /events
que vai chamar a action store
no mesmo controller que utilizaremos para processar o formulário que ficará na view event/create
B – Adicione o seguinte método em app/Controllers/Http/EventController.js
1 2 3 4 5 6 7 8 |
... class EventController { ... create({ view }) { return view.render('event.create') } } ... |
O método está renderizando uma view create. Vamos criá-la.
C – Execute o comando para criamos a view event/create
1 |
adonis make:view event.create |
D – E acrescente o seguinte conteúdo na view resources/views/event/create
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@layout('layouts.main') @section('content') <h1 class="title is-4">Novo Evento</h1> <form action="{{ route('events.store') }}" method="POST"> {{ csrfField() }} <div class="columns is-multiline"> <div class="column is-four-fifths field"> <label for="title">Título</label> <div class="control"> <input input="text" class="input" name="title" /> </div> </div> <div class="column is-one-fifth field"> <label for="date">Data</label> <div class="control"> <input input="date" class="input" name="date" /> </div> </div> <div class="column is-full field"> <label for="description">Descrição</label> <div class="control"> <textarea input="description" class="textarea" name="description" style="resize: none"> </textarea> </div> </div> <div class="column is-full field"> <button type="submit" class="button is-success">Salvar</button> <a href="{{ route('events.index') }}" class="button is-light">Cancelar</a> </div> </div> </form> @endsection |
É um formulário HTML normal, com helpers do Adonis, como o route
, que já vismos e a chamada ao csrfFields()
, que acrescenta campos de chave para processar o formulário evitando ataques CSRF.
E – Após o formulário, atualize o EventController em app/Controllers/Http/EventController.js
para o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
'use strict' const Event = use('App/Models/Event'); class EventController { async index({ view }) { const events = await Event.all() return view.render('event.index', { events: events.toJSON() }) } create({ view }) { return view.render('event.create') } async store({ request, response }) { const eventParams = request.only(['title', 'date', 'description']) await Event.create({ ...eventParams }) return response.route('events.index') } } module.exports = EventController |
Primero importamos o model Event.
Em seguida, atualizamos a action index para carregar todos os eventos e renderizar a view events.index
passando a variável events
como parâmetro. Nesta renderização, estamos utilizando um toJSON()
para que o objeto vá serializado para a view.
Também criamos o método store para salvar o Evento e redirecionar para a lista de eventos
F – Agora vamos atualizar a view index de eventos para listar os eventos cadastrados. Atualize o código de resources/views/events/index.edge
para
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@layout('layouts.main') @section('content') <div class="columns"> <div class="column is-fourth-fifth"> <h1 class="title is-4">Meus eventos</h1> </div> <div class="column is-one-fifth"> <a href="{{ route('events.create') }}" class="button is-success">Novo Evento</a> </div> </div> @if(events.length > 0) <div class="columns"> @each(event in events) <div class="column is-one-third"> <div class="box"> <p class="is-size-5"> <strong>{{ event.title }}</strong> </p> <p class="is-size-7"> <i>[{{ event.date }}]</i> </p> <br /> <p class="is-6">{{ event.description }}</p> </div> </div> @endeach </div> @else <h1 class="subtitle is-5">Você não tem eventos cadastrados</h1> @endif @endsection |
Adicionamos na view um botão Novo Evento para irmos para formulário de criação do evento. Juntamente com isso, adicionamos um bloco @if do Edge para verificar se existe algum evento e caso não exista (bloco @else) ele mostrará uma mensagem.
Se existir algum evento na lista, utilizamos o bloco @each para percorrer cada evento na lista e exibir. Perceba que utilizamos a notação {{ event.<propridade> }} do Edge para mostrar o valor na view.
2.8 Editar Evento
[Branch: edit_event]
Para editar o evento, vamos utilizar components, uma técnica de dividir trechos de uma view em partes menores e possibilitar o reuso.
A – Primeiro, vamos criar uma rota para exibir a view de editar e outra para submetermos o formulário de atualização. Vamos adicionar o seguinte em start/routes.js
1 2 |
Route.get('/events/:id/edit', 'EventController.edit').as('events.edit') Route.patch('/events/:id', 'EventController.update').as('events.update') |
Criamos uma nova /events/:id/edit
e outra /events/:id
. Esse :id
é um recurso das rotas do Adonis para que ela seja considerada um parâmetro da rota. Então, por exemplo, se chamarmos a rota /events/1/edit
o Adonis vai cair nesta rota /events/:id/edit
e nos parâmetros será enviado um campo id com valor 1.
B – Próximo passo é criamos as actions em nosso EventController. Então vamos adicionar em app/Controllers/Http/EventController.js
os métodos edit e update abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
... class EventController { ... async edit({ params, view }) { const event = await Event.find(params.id) return view.render('event.edit', { event: event }) } async update({ params, request, response }) { const event = await Event.find(params.id) const eventParams = request.only(['title', 'date', 'description']) event.merge({ ...eventParams }) await event.save() return response.route('events.index') } ... } ... |
No edit, estamos utilizando o params, que também vem no método e nele está o parâmetro que passamos via URL. Estamos utilizando o id que esta lá para carregarmos o nosso evento com o método find e passamos como parâmetro da renderização da view event.edit
, que criaremos mais pra frente.
No update estamos carregando o evento da mesma forma que em edit, pegando os parâmetros do formulário, fazendo um merge com os atributos do evento que carregando (dessa forma ele altera do que estiver diferente no objeto event) e chamando o método save (que processa do banco de dados). Após salvar, redicionamos de volta pra lista de eventos.
C – Vamos agora criar noss componente. Pra isso, criaremos uma view form
para o formulário de evento com o comando
1 |
adonis make:view event.form |
D – E mover o formulário da view resources/views/event/create
para resorces/views/event/form
com algumas alterações. Inclua o seguinte conteúdo na view resources/views/edit/form
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<form action="{{ action }}?_method={{ method }}" method="POST"> {{ csrfFields }} <div class="columns is-multiline"> <div class="column is-four-fifths field"> <label for="title">Título</label> <div class="control"> <input input="text" class="input" name="title" value="{{ event.title || '' }}" /> </div> </div> <div class="column is-one-fifth field"> <label for="date">Data</label> <div class="control"> <input input="date" class="input" name="date" value="{{ event.date || '' }}" /> </div> </div> <div class="column is-full field"> <label for="description">Descrição</label> <div class="control"> <textarea input="description" class="textarea" name="description" style="resize: none">{{ event.description || '' }}</textarea> </div> </div> <div class="column is-full field"> <button type="submit" class="button is-success">Salvar</button> <a href="{{ route('events.index') }}" class="button is-light">Cancelar</a> </div> </div> </form> |
Perceba que o atributo action do form agora possui uma chamada a action
e method
. Estas duas variáveis serão declaradas quando chamarmos o component mais pra frente. A variável action
precisará receber a rota em que o form será enviado e a method
, o tipo de método HTTP que será enviado. O que fizemos neste formulário com _method=...
é uma técnica chamada de Method Spoofing. Isso ocorre porque os formulários HTML são capazes apenas de ligar com requisições GET e POST, então o Adonis aceita o parâmetro _method
como o método que queremos chamar para a rota.
Outras duas variáveis importantes são a csrfFields, que utilizamos para enviar os campos de CSRF do formulário e a event, onde enviaremos o evento. Perceba que nos campos estamos atribuindo o valor de uma propriedade do evento ou vazio. Estamos fazendo isso porque este formulário será utilizando tanto pra view create quando para a edit e na create, o evento ainda não existe, portanto event retorna undefined.
E – Próximo passo agora é atualizar a view resources/views/event/create.edge
para
1 2 3 4 5 |
@layout('layouts.main') @section('content') <h1 class="title is-4">Novo Evento</h1> @!component("event.form", action = route("events.store"), method = "POST", event = event, csrfFields = csrfField()) @endsection |
Perceba que estamos chamando o componente event.form
que criamos anteriormente com @!component
e passando os parâmetros que utilizamos neste componente.
F – E vamos também criar a view event.edit
1 |
adonis make:view event.edit |
G – E acrescentar o seguinte conteúdo em resources/view/event/edit.edge
1 2 3 4 5 |
@layout('layouts.main') @section('content') <h1 class="title is-4">Editar Evento</h1> @!component("event.form", action = route("events.update", { id: event.id }), method = "PATCH", event = event, csrfFields = csrfField()) @endsection |
Perceba que é semelhate à view de create, porém tem o título diferente e estamos passando a rota de update, com o verbo HTTP patch.
H – Vamos também atualizar a view resources/views/event/index.edge
para acrescentar um botão de Editar em cada Evento listado. Atualize o código para
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@layout('layouts.main') @section('content') <div class="columns"> <div class="column is-fourth-fifth"> <h1 class="title is-4">Meus eventos</h1> </div> <div class="column is-one-fifth"> <a href="{{ route('events.create') }}" class="button is-success">Novo Evento</a> </div> </div> @if(events.length > 0) <div class="columns"> @each(event in events) <div class="column is-one-third"> <div class="box"> <p class="is-size-5"> <strong>{{ event.title }}</strong> </p> <p class="is-size-7"> <i>[{{ event.date }}]</i> </p> <br /> <p class="is-6">{{ event.description }} <p> <br /> <p> <a href="{{ route('events.edit', { id: event.id }) }}" class="button is-small is-link">Editar</a> </p> </div> </div> @endeach </div> @else <h1 class="subtitle is-5">Você não tem eventos cadastrados</h1> @endif @endsection |
2.9 Remover evento
[Branch: remove_event]
A – Para remover o evento, primeiro vamos criar a rota para acessar. Adicione em start/routes.js
1 2 |
... Route.delete('/events/:id', 'EventController.destroy').as('events.destroy') |
B – Agora vamos para a action para excluir no controller de Eventos. Adicione em app/Controllers/Http/EventController.js
. Adicione a action destroy da seguinte forma
1 2 3 4 5 6 7 8 9 10 |
... class EventController { ... async destroy({ params, response }) { const event = await Event.find(params.id) await event.delete() return response.route('events.index') } } ... |
C – Após isso, vamos adicionar um botão de excluir na lista de eventos em resources/views/event/index.edge
. Atualize a view para
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
@layout('layouts.main') @section('content') <div class="columns"> <div class="column is-fourth-fifth"> <h1 class="title is-4">Meus eventos</h1> </div> <div class="column is-one-fifth"> <a href="{{ route('events.create') }}" class="button is-success">Novo Evento</a> </div> </div> @if(events.length > 0) <div class="columns"> @each(event in events) <div class="column is-one-third"> <div class="box"> <p class="is-size-5"> <strong>{{ event.title }}</strong> </p> <p class="is-size-7"> <i>[{{ event.date }}]</i> </p> <br /> <p class="is-6">{{ event.description }} <p> <br /> <p> <form action="{{ route('events.destroy', { id: event.id }) }}?_method=DELETE" method="POST"> {{ csrfField() }} <a href="{{ route('events.edit', { id: event.id }) }}" class="button is-small is-link">Editar</a> <button class="button is-small is-danger">Remover</button> </form> </p> </div> </div> @endeach </div> @else <h1 class="subtitle is-5">Você não tem eventos cadastrados</h1> @endif @endsection |
Para evitar, por agora, mexer em reucrsos de frond-end para fazer o Method Spoofing fora de um formulário, criamos um mini formulário dentro de cada linha do evento que vai conter apenas o botão para ir para a página de edit e outro botão para remover que faz o submit do formulário que possui um Method spoofing com o verbo HTTP DELETE.
2.10 Marcar ‘Eu vou’
[Branch: people_count]
Agora, a última parte é adicionarmos os eventos na página Home para que possam clicar em “Eu vou” e aumentar a contagem de pessoas.
A – Antes de tudo, precisamos adicionar um campo de contagem na tabela de eventos e vamos fazer isso criando uma migration com o comando:
1 |
adonis make:migration events --action select |
B – Vamos deixar a migration database/migrations/<timestamp>_events_schema.js
que foi criada com o seguinte conteúdo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
'use strict' /** @type {import('@adonisjs/lucid/src/Schema')} */ const Schema = use('Schema') class EventsSchema extends Schema { up() { this.table('events', (table) => { table.integer('people_count').defaultTo(0) }) } down() { this.table('events', (table) => { table.dropColumn('people_count') }) } } module.exports = EventsSchema |
C – Agora vamos executar esta migration com o comando
1 |
adonis migration:run |
D – Agora vamos criar um controller HTTP chamado PresenceController que utilizaremos contar mais uma pessoa no Evento. Execute o comando:
1 |
adonis make:controller presence |
Não esqueça de selecionar a opção HTTP Controller
E – E adicione o seguinte conteúdo em app/Controllers/Http/PresenceController.js
1 2 3 4 5 6 7 8 9 10 11 |
'use strict' const Event = use('App/Models/Event') class PresenceController { async update({ params, response }) { const event = await Event.find(params.eventId) event.people_count += 1 event.save() response.route('home.index') } } module.exports = PresenceController |
F – Agora vamos adicionar mais uma rota em start/routes.js
1 2 |
... Route.patch('/events/:eventId/presence', 'PresenceController.update').as('presences.update') |
H – Com o controller de presença pronto, vamos criar também um para a página Home. Execute:
1 |
adonis make:controller home |
Não esqueça de selecionar a opção HTTP Controller
I – Acrescentaremos o seguinte conteúdo em app/Controllers/Http/HomeController.js
1 2 3 4 5 6 7 8 9 |
'use strict' const Event = use('App/Models/Event') class HomeController { async index({ view }) { const events = await Event.all() return view.render('home.index', { events: events.toJSON() }) } } module.exports = HomeController |
J – Não podemos esquecer de alterar a rota /
. No arquivo start/routes.js
, substitua:
1 |
Route.on('/').render('home.index') |
Por
1 |
Route.get('/', 'HomeController.index').as('home.index') |
H – E vamos, por último alterar a view resoures/views/home/index.edge
para
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@layout('layouts.main') @section('content') @if(events.length > 0) <div class="columns"> @each(event in events) <div class="column is-one-third"> <div class="box"> <p class="is-size-5"> <strong>{{ event.title }}</strong> </p> <p class="is-size-7"> <i>[{{ event.date }}]</i> <i>[{{ event.people_count }} pessoas]</i> </p> <br /> <p class="is-6">{{ event.description }} <p> <br /> <p> <form action="{{ route('presences.update', { eventId: event.id }) }}?_method=PATCH" method="POST"> {{ csrfField() }} <button type="submit" class="button is-small is-success">Eu vou! o/</button> </form> </p> </div> </div> @endeach </div> @else <h1 class="subtitle is-5">Não existem eventos públicos cadastrados</h1> @endif @endsection |
Adicionamos aqui mais um form com Method spoofing para poder mascarar um verbo HTTP PATCH dentro de um POST
2.11 Testando o app
Com esta última atualização da view home.index
concluídos nossa pequena aplicação utilizando o Adonis. Veja como ficou:

Conclusão
Neste artigo tivemos uma introdução prática ao AdonisJs através da criação de um APP para gerenciamento de eventos.
Pra quem quiser, aqui está o link do Github do nosso projeto: https://github.com/escola-de-javascript/introducao_adonisjs
A documentação do Adonis está bem organizada e você pode dar uma navegada no link para poder aprender mais sobre o que ele tem de incrível
https://adonisjs.com/docs/4.1/installation
Comente a baixo pra sabermos se você gostou do artigo e se gostaria de uma continuação. Se tivermos respostas bem positivas, vou continuar esse APP e transformá-lo numa série sobre o Adonis abordando estes e outros assuntos.
Obrigado pessoal,
Até o próximo artigo o/
Parabéns por esta publicação, muito bem explicado, gostaria de ver á continuação desse App. Abraços
Obrigado pelo Feedback o/
Parabéns Pela iniciativa.
Obrigado pelo comentário 🙂
Parabéns pela publicação, curti muito. Acompanhei e consegui já ter uma noa noção do AdonisJs. Algum problema em adicionar este projeto em meu github?
E ai Ricelli, tudo bem?
Ficamos felizes que tenha curtido 🙂
Pode adicionar sim,
Grande abraço
Quero agradecer pela grande ajuda que sua publicação me trouxe, mas também gostaria de pedir uma ajuda se não for pedir muito. Estou com dificuldade na parte de autenticação com jwt, até consegui fazer o registro do usuário, obter o token jwt e armazenar no localStorage, a minha dúvida é como passar esse token nas chamadas das rotas dentro das views, se puder me ajudar eu ficaria muito grato.