Primeirona: segunda parte
Table of Contents
1 Primeirona (continuação)
1.1 Completando o CRUD
- Remoção
Observando as rotas, vemos que
delete
está redirecionado paradestroy
.Vamos alterar a visão dos apelidos e incluir um link para remoção. Aproveitemos também para usar uma tabela ao invés de uma lista.
O
index.html.haml
fica assim:%h1 Apelidos cadastrados %table{border: 3} %tr %th Nome %th Apelido %th - @apelidos.each do |ap| %tr %td= ap.nome %td= ap.apelido %td = link_to 'Apagar', apelido_path(ap), method: :delete, data: {confirm: 'Certeza?'} = link_to 'Voltar', new_apelido_path
A opção
:data
abre uma janela de confirmação.Falta ajustar o método
destroy
def destroy @apelido = Apelido.find(params[:id]) @apelido.destroy redirect_to apelidos_path end
- Edição
Como edição implica em alteração no banco, é importante restringir quais atributos podem ser modificados. Já fizemos isso na criação onde exigimos a presença e liberamos os campos permitidos. Vamos transferir o teste para uma única função privada.
Junto com a edição, acertamos a atualização (update). As novas versões são estas:
def edit @apelido = Apelido.find(params[:id]) end def update @apelido = Apelido.find(params[:id]) if @apelido.update(apelido_params) redirect_to @apeliddo else render 'edit' end end private def apelidos_params params.require(:apelido).permit(:nome, :apelido) end
O
index.html.haml
precisa mostrar campos de edição também:%h1 Apelidos cadastrados %table{border: 3} %tr %th Nome %th Apelido %th - @apelidos.each do |ap| %tr %td= ap.nome %td= ap.apelido -# link para edição %td = link_to 'Editar', edit_apelido_path(ap) %td = link_to 'Apagar', apelido_path(ap), method: :delete, data: {confirm: 'Certeza?'} = link_to 'Voltar', new_apelido_path
No
show.html.haml
também%h1 Pronto! %p %i= @apelido.nome já pode ser chamado de %i= @apelido.apelido %p = link_to 'Editar', edit_apelido_path(@apelido) = link_to 'Novo', new_apelido_path = link_to 'Lista', apelidos_path
A edição é bastante parecida com a criação. O princípio DRY sugere reaproveitar o código tanto quanto possível. Faremos com a visão.
O formulário é o mesmo, usaremos uma página parcial, que nada mais é do que um arquivo que começa com
_
.Este é o
_formulario.html.haml
= form_with model: @apelido, local: true do |f| %table %tr %td = f.label :nome %td = f.text_field :nome %tr %td = f.label :apelido %td = f.text_field :apelido = f.submit t(:save)
Com ele, tanto
new.html.haml
comoedit.html.haml
ficam mais simples. Respectivamente:%h1 Novo apelido = render 'formulario' = link_to 'Voltar', apelidos_path
e
%h1 Editando um apelido = render 'formulario' = link_to 'Voltar', apelidos_path
1.2 Validação
Garantir que os dados são aceitáveis e tratar eventuais erros.
Isso é feito no model. Vamos colocar as seguintes restrições:
- nome tem que ter pelo menos 3 letras
- nome e apelido têm que estar presentes
O método validates
cuida disso.
class Apelido < ApplicationRecord validates :nome, presence: true, length: {minimum: 3} validates :apelido, presence: true end
Verifique que os registros inválidos não são salvos.
Mas agora precisamos agir melhor quando um erro ocorre. Vamos
acertar algumas coisas no controlador, essencialmente o create
e o
new
.
def create @apelido = Apelido.new(apelidos_params) # salva @apelido.save # Testamos se conseguiu salvar if @apelido.save redirect_to @apelido else # tenta de novo render 'new' end def new # como tem um render new no create precisamos de um @apelido definido @apelido = Apelido.new end
Precisamos também avisar o usuário do que deu errado. Isso fica no view.
= form_with model: @apelido, local: true do |f| - @apelido.errors.any? %ul - @apelido.errors.full_messages.each do |msg| %li= msg %table %tr %td = f.label :nome %td = f.text_field :nome %tr %td = f.label :apelido %td = f.text_field :apelido = f.submit 'Salva'
1.3 Internacionalização
As mensagens de erro estão em inglês. Podemos acertar com a classe
I18n
. Os ajustes ficam em config/application.rb
. Primeiro
precisamos habilitar os locales disponíveis, depois escolher o
default.
Coloque essas linhas em config/application.rb
:, depois dos
require~s e do ~Bundler
.
# Whitelist locales available for the application I18n.available_locales = [:en, :pt] # Set default locale to something other than :en I18n.default_locale = :pt
Agora, precisamos definir as traduções. Veja um exemplo e a
documentação em config/locales/en.yml
. Um arquivo YAML (extensão
yml
) é uma espécie simplificada de JSON e representa uma árvore com
os contextos de cada palavra.
Crie o arquivo pt.yml
no diretório config/locales/
con o conteúdo
abaixo e complete para todos os casos:
pt: save: 'Salva' edit: 'Editar' back: 'Voltar' delete: 'Apagar' new: 'Novo' ready: 'Pronto' may_now: 'já pode ser chamado de' activerecord: errors: messages: blank: "falta alguma coisa aqui" models: apelido: attributes: nome: too_short: "curto demais"
Veja que as folhas são as chaves para tradução, assim 'save' será
traduzida para 'Salva'. Onde uma tradução for necessária use t(
chave )
.
Os casos mais específicos têm precedência sobre os mais gerais. Para saber quais chaves ainda precisam ser incluídas, veja as mensagens de erro quando o Rails não conseguir traduzir.
Prepare o en.yml
também
en: may_call: 'may already be called'
Finalemente, ajuste todas as mensagens com traduções. Veja este
exemplo no index.html.haml
(aproveitei para colocar uma borda na
tabela, a título de ilustração).
%table{border: 3} %tr %th Nome %th Apelido %th - @apelidos.each do |ap| %tr %td= ap.nome %td= ap.apelido -# link para edição %td = link_to t(:edit), edit_apelido_path(ap) %td = link_to t(:delete), apelido_path(ap), method: :delete, data: {confirm: t('sure')} = link_to t(:back), new_apelido_path
Atenção, para ativar novos locales, é preciso reiniciar o servidor.
1.4 CSS (Cascade Style Sheets)
Para deixar a página mais apresentável, adicionamos CSS ao HTML. No nosse caso, usaremos SASS (Syntatically Awesome Style Sheets), que é uma extensão do CSS com diversas vantagems e, claro, continuaremos a usar o HAML.
SASS permite o uso de variáveis, aninhamento e mixins entre outras coisas. A sintaxe básica é a mesma da CSS.
Para detalhes de CSS veja, por exemplo, https://www.w3schools.com/css/default.asp.
Para SASS: https://sass-lang.com/documentation/file.SASS_REFERENCE.html
Os arquivos de formatação ficam em app/assets/stylesheets
.
Vamos melhorar (ok, brincar com) nossa página principal. Primeiro
centralizamos a tabela. Basta colocar %center
no início do arquivo
e identar todas as linhas.
Aproveitamos para por um título na página. Os títulos pertencerão à classe titulo.
%center %h1.titulo=t(:list) %table{border: 3} %tr %th Nome %th Apelido - @apelidos.each do |ap| %tr %td= ap.nome %td= ap.apelido -# link para edição %td = link_to t(:edit), edit_apelido_path(ap) %td = link_to t(:delete), apelido_path(ap), method: :delete, data: {confirm: t('sure')} = link_to t(:back), new_apelido_path
Em app/assets/stylesheets
acertamos o estilo. Brinque à vontade.
body { background: gold; } table { color: blue; } h1.titulo { text-shadow: 2px 3px grey; }
Uma variação interessante é usar um serviço de CSS como o
bootstrap
.
Inclua no final do Gemfile:
gem 'bootstrap-sass', '~> 3.4.1' gem 'sassc-rails', '>= 2.1.0' gem 'jquery-rails'
Não se esqueça do bundle install
.
Atualize o application.scss
, passando todos os estilos para lá.
/* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's * vendor/assets/stylesheets directory can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the bottom of the * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS * files in this directory. Styles in this file should be added after the last require_* statement. * It is generally better to create a new file per style scope. * * retirei o '=' das próximas duas linhas * require_tree . * require_self */ @import "bootstrap-sprockets"; @import "bootstrap"; @import "apelidos"
Para ativar javascript, que usaremos mais tarde, altere
app/assets/applications.js
colocando //require
bootstrap-sprockets
na penúltima linha (require_tree
):
// This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's // vendor/assets/javascripts directory can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. JavaScript code in this file should be added after the last require_* statement. // // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // //= require rails-ujs //= require activestorage //= require turbolinks //= require bootstrap-sprockets //= require_tree .
Agora experimente novas versões
application.html.haml
!!! %html %head %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ %title Primeirona = csrf_meta_tags = csp_meta_tag = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %body %nav.navbar.navbar-default.container-fluid .collapse.navbar-collapse .navbar.navbar-nav.navbar-right .btn= link_to t(:list), apelidos_path .btn= link_to t(:new), new_apelido_path = yield
index.html.haml
%center %h1.titulo=t(:list) %table.table-hover.table-bordered %thead %tr %th Nome %th Apelido - @apelidos.each do |ap| %tr %td= ap.nome %td= ap.apelido -# link para edição %td = link_to edit_apelido_path(ap) do %span.glyphicon.glyphicon-pencil %td = link_to apelido_path(ap), method: :delete, data: {confirm: t('sure')} do %span.glyphicon.glyphicon-remove = link_to t(:back), new_apelido_path
Arrume os outros