CRUD básico com Vue.js e json-server #5
Com a parte de leitura da tela pronta (ler os dados, paginar e realizar a busca), podemos iniciar a codificação para inserir e editar as cervejarias. Esta codificação irá alterar bastante o código Cervejarias.vue
, de forma que iremos a princípio mostrar quais são estas modificações, e no final do artigo mostrar o código por completo.
Para inserir ou editar um item, usaremos o recurso de janela modal do bulma. A criação da janela é feita através de uma div
com a classe modal
, e a sua visibilidade é controlada através da classe is-active
:
<div class="modal" :class="{'is-active':showModal}">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Cervejaria: </p>
<button class="delete" @click.prevent="showModal=false"></button>
</header>
<section class="modal-card-body">
<div class="columns">
<div class="column">
<label class="label">Nome</label>
<p class="control">
<input class="input" type="text" placeholder="Text input" v-model="selected.name">
</p>
</div>
<div class="column">
<label class="label">Código</label>
<p class="control">
<input class="input" type="text" placeholder="Código" v-model="selected.code">
</p>
</div>
</div>
<label class="label">Descrição</label>
<p class="control">
<textarea class="textarea" placeholder="Textarea" v-model="selected.descript"></textarea>
</p>
<label class="label">Website</label>
<p class="control">
<input class="input" type="text" placeholder="Text input" v-model="selected.website">
</p>
</section>
<footer class="modal-card-foot">
<a class="button is-primary" @click.prevent="saveBrewery">Salvar</a>
<a class="button" @click.prevent="showModal=false">Cancelar</a>
</footer>
</div>
</div>
Veja que, :class="{'is-active':showModal}"
controla a visibilidade da janela através da variável showModal
, que deve ser adicionada a lista de variáveis do componente:
data () {
return {
isLoading: false,
title: 'Vue.js Crud',
search: '',
breweries: [],
page: 1,
total: 0,
selected: {},
itensPerPage: 10,
showModal:false
}
O formulário da cervejaria possui poucos campos, para que esta parte inicial não seja tão complexa. O formulário resulta em algo semelhante à figura a seguir:
Neste formulário, temos apenas os campos Nome, Código, Descrição e Website.
Para que este formulário apareça, é necessário alterar a variável showModal
. Fazemos isso no botão de edição de cada linha da tabela, da seguinte forma:
<a href="#" @click.prevent="editBrewery(brewery)">
<i class="fa fa-edit"></i>
</a>
O método editBrewery
contém o seguinte código:
methods:{
...,
editBrewery(brewery){
this.selected=brewery
this.showModal = true;
},
...
Veja que alteramos a variável selected
, repassando o objeto brewery
que é o mesmo objeto que representa a “linha” da “tabela”.
Agora o objeto selected possui os dados da cervejaria que está sendo editada. Quando o usuário clicar no botão para fechar a janela, ou no botão cancelar, executamos o seguinte código:
<a class="button" @click.prevent="showModal=false">Cancelar</a>
Ou seja, usamos a reatividade do Vue para “fechar” a janela, na verdade nós apenas a escondemos.
O botão salvar irá realizar a edição ou inclusão da cervejaria, veja:
methods:{
...,
saveBrewery(){
if (this.selected.id!=null){ //EDIT
this.$http.put(`/breweries/${this.selected.id}`,this.selected).then(
response=>{
this.$set('selected',{})
this.$set('showModal',false)
},
error=>{
console.error(error)
}
).finally(
this.loadBreweries()
)
}
else
{ //NEW
this.$http.post(`/breweries`,this.selected).then(
response=>{
this.$set('selected',{})
this.$set('showModal',false)
},
error=>{
console.error(error)
}
).finally(
this.loadBreweries()
)
}
},
...
Neste código, verificamos a existência do {selected.id}
, pois é ele que define se estamos incluindo um novo item ou editando. Se for uma edição, ou seja, existe um selected.id
, usamos o vue resource e fazemos uma chamada PUT a url /breweries/id
repassando o selected e, quando o servidor responder, pode-se esconder a janela modal novamente. Veja que usamos o método finally
para chamar novamente o loadBreweries
, para recarregar a tabela.
Se for um novo registro, realizamos um POST repassando o objeto selected
.
Para finalizar, basta adicionar um botão “Novo” que irá criar uma instância vazia do selected
e abrir a janela modal, veja:
<a class="button is-info" @click.prevent="newBreweries">Novo</a>
e
newBreweries(){
this.selected={}
this.showModal = true;
},
O código completo do componente é exibido a seguir:
<template>
<a class="fixo button is-large is-danger is-loading" v-show="isLoading">Loading</a>
<div class="container">
<h1 class="title"></h1>
<div class="columns">
<div class="column is-5">
<p class="control has-addons">
<input class="input is-expanded" type="text" placeholder="Procure pelo nome" v-model="search">
<a class="button is-info" @click.prevent="searchBreweries">Search</a>
</p>
</div>
<div class="column is-6">
</div>
<div class="column is-1">
<a class="button is-info" @click.prevent="newBreweries">Novo</a>
</div>
</div>
<div class="columns">
<div class="column is-12">
<table class="table is-narrow is-bordered">
<thead>
<th>Nome</th>
<th>Cidade</th>
<th>Telefone</th>
<th>Mais</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
<tr v-for="brewery in breweries">
<td></td>
<td></td>
<td></td>
<td class="is-icon">
<a href="#">
<i class="fa fa-map-marker"></i>
</a>
<a href="#">
<i class="fa fa-plus-circle"></i>
</a>
</td>
<td class="is-icon">
<a href="#" @click.prevent="editBrewery(brewery)">
<i class="fa fa-edit"></i>
</a>
<a href="#">
<i class="fa fa-trash"></i>
</a>
</td>
</tr>
</tbody>
</table>
<Pagination :total="total" :page="page" :itens-per-page="itensPerPage" @change-page="onChangePage"></Pagination>
</div>
</div>
</div>
<div id="modal_brewery" class="modal" :class="{'is-active':showModal}">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Cervejaria: </p>
<button class="delete" @click.prevent="showModal=false"></button>
</header>
<section class="modal-card-body">
<div class="columns">
<div class="column">
<label class="label">Nome</label>
<p class="control">
<input class="input" type="text" placeholder="Text input" v-model="selected.name">
</p>
</div>
<div class="column">
<label class="label">Código</label>
<p class="control">
<input class="input" type="text" placeholder="Código" v-model="selected.code">
</p>
</div>
</div>
<label class="label">Descrição</label>
<p class="control">
<textarea class="textarea" placeholder="Textarea" v-model="selected.descript"></textarea>
</p>
<label class="label">Website</label>
<p class="control">
<input class="input" type="text" placeholder="Text input" v-model="selected.website">
</p>
</section>
<footer class="modal-card-foot">
<a class="button is-primary" @click.prevent="saveBrewery">Salvar</a>
<a class="button" @click.prevent="showModal=false">Cancelar</a>
</footer>
</div>
</div>
</template>
<script>
import Pagination from './Pagination.vue'
export default {
data () {
return {
isLoading: false,
title: 'Vue.js Crud',
search: '',
breweries: [],
page: 1,
total: 0,
selected: {},
itensPerPage: 10,
showModal:false
}
},
components: {
Pagination
},
methods: {
onChangePage(page){
this.page = page
this.loadBreweries()
},
showLoading(){
this.isLoading=true;
},
hideLoading(){
this.isLoading=false;
},
loadBreweries(){
let t = this
this.showLoading()
let start = (this.page * this.itensPerPage) - (this.itensPerPage)
let end = this.page * this.itensPerPage
let qString = "";
if (this.search){
qString = `&q=${this.search}`
}
this.$http.get(`/breweries?_start=${start}&_end=${end}${qString}`).then(
response=>{
t.breweries = response.json()
t.total = response.headers['X-Total-Count']
},
error=>{
console.log(error)
}).finally(function () {
t.hideLoading();
})
},
searchBreweries(){
this.loadBreweries()
},
newBreweries(){
this.selected={}
this.showModal = true;
},
editBrewery(brewery){
this.selected=brewery
this.showModal = true;
},
saveBrewery(){
if (this.selected.id!=null){ //EDIT
this.$http.put(`/breweries/${this.selected.id}`,this.selected).then(
response=>{
this.$set('selected',{})
this.$set('showModal',false)
},
error=>{
console.error(error)
}
).finally(
this.loadBreweries()
)
}
else
{ //NEW
this.$http.post(`/breweries`,this.selected).then(
response=>{
this.$set('selected',{})
this.$set('showModal',false)
},
error=>{
console.error(error)
}
).finally(
this.loadBreweries()
)
}
}
},
created(){
this.loadBreweries();
},
}
</script>
<style>
.fixo{float: right; margin-right: 10px; margin-top: 0px; z-index: 1000;}
</style>
Caso você deseje ver as alterações desta parte em relação ao artigo anterior, clique neste link para ver o diff no github.
O resultado da edição é exibido a seguir: