Introdução

Algo que meus clientes pedem muito é a possibilidade de editar ou comentar tabelas e gravá-las. A solução que tenho oferecido é manter a tabela numa base de dados SQL, chamá-la via shinyapp e possibilitar a sua edição com o pacote DT. Vamos mostrar o passo a passo.

Criando a tabela

Vamos primeiramente colocar o dataset iris numa base SQL:

conn <- DBI::dbConnect(....)

DBI::dbWriteTable(conn,"iris",iris)

Preparando a tabela

Agora vamos para o terminal da base SQL e criar duas colunas. Uma coluna chamada iris_id tipo auto increment (MySQL ou sqlight) ou serial (PostgreSQL), e outra coluna chamada comentario, que será a coluna que receberá os comentários do cliente. Naturalmente, essa coluna é dispensável se você quiser editar as demais colunas.

Eu vou utilizar SERIAL, porque trabalho com PostgreSQL, mas vc saberá usar auto increment em outro gerenciador de base de dados.

ALTER TABLE iris ADD COLUMN iris_id SERIAL;
ALTER TABLE iris ADD COLUMN comentario text;
UPDATE iris SET comentario = '    '; -- Incluí espaços em branco

Shinyapp

Feito isso, vamos agora criar um shinyapp para visualizar a tabela para poder editá-la.

Pacotes

Antes do ui, coloque os pacotes necessários:

library(shiny)
library(DBI) ## para realizar a conexão com a base
library(dbx) ## para atualizar a tabela
library(DT)  ## para editar a tabela
library(dplyr) ## para ler e ordenar a tabela a cada leitura 

UI

O ui é bem simples, é suficiente colocar um botão de salvar para poder salvar as edições. A menos que você queira realizar outras operações.

ui <-  fluidPage(
       titlePanel("Atualização de tabelas em SQL"),

     sidebarLayout(
         sidebarPanel(
         actionbutton("salvar","Salvar")
         ),
         
         mainPanel(
         
        tabPanel("View", br(), DT::DTOutput("tabela"))
         
         )
     )
)

Server

O server é mais complicadinho, mas nada do outro mundo. O pacote DT contêm funções que nos ajudam nisso. Vamos colocar cada parte em um chunk para, mais adiante, juntar tudo.

Conexão

Para realizar a conexão, eu sugiro usar o pacote DBI em vez do pool, porque o pacote dbx não aceita objetos do pool


### Conexão

observe({
conn <- DBI::dbConnect(....)

onStop(function(){
DBI::dbDisconnect(conn)
})

}

Leitura da tabela

Agora vamos importar a tabela e colocá-la num objeto reativo para, em seguida, transformá-lo num data.frame.

fonte <- reactive({

tbl(conn,"iris") %>% 
   arrange(iris_id)

})


dados <- fonte()
  

Tabela editável

Vamos agora criar uma tabela editável. No caso, editaremos apenas a coluna comentário, que é a última, de um data.frame de sete colunas. Note que a indexação do DT::datatable começa no zero. Por isso, eu preciso desabilitar as edição das colunas de zero a 5.

output$tabela <- DT::renderDT(
    datatable(dados,
              rownames = FALSE,
              editable = list(target="cell",disable=list(columns=c(0:5))),
              selection = 'none')
  )

Edição da tabela

A fim de editar a tabela, criar um proxy da tabela chamada.

Note que para editar a tabela, o dataTableProxy inlclui no input um elemento chamado tabela_cell_edit. Nele se encontram o índice da coluna, da linha e o novo valor da célula. A primeira parte, tabela, é o nome que você colocou no output ao chamar o renderDT.

Eu vou usar a função editData, que já faz todo o serviço de chamar as informações para edição (info) e atualizar a tabela. No entanto, se você quiser mais flexibilidade, veja as opções usando a função replaceData.

proxy1 <- dataTableProxy("tabela")


observeEvent(input$tabela_cell_edit, {
    info = input$tabela_cell_edit
    str(info)  ## Mera checagem se foi criada uma tabela com índice da                    ## coluna, da linha, e o novo valor da célula.
    
  dados <<-  editData(dados,info,proxy1,rownames = F)
  
  iris_id <- dados[info$row,'iris_id']
  comentario <- dados[info$row,'comentario']
  records <-  data.frame(iris_id,comentario)
  
  observeEvent(input$salvar,{
    
    dbx::dbxUpdate(conn,"iris",records,where_cols = c("iris_id"))
  })
 
})


})

Server completo

Agora vamos colocar tudo no server


server <- function(input,output, session){
### Conexão

observe({
conn <- DBI::dbConnect(....)

onStop(function(){
DBI::dbDisconnect(conn)
})

}

fonte <- reactive({

tbl(conn,"iris") %>% 
   arrange(iris_id)

})


dados <- fonte()

output$tabela <- DT::renderDT(
    datatable(dados,
              rownames = FALSE,
              editable = list(target="cell",disable=list(columns=c(0:5))),
              selection = 'none')
  )


proxy1 <- dataTableProxy("tabela")


observeEvent(input$tabela_cell_edit, {
    info = input$tabela_cell_edit
    str(info)  ## Mera checagem se foi criada uma tabela com índice da                    ## coluna, da linha, e o novo valor da célula.
    
  dados <<-  editData(dados,info,proxy1,rownames = F)
  
  iris_id <- dados[info$row,'iris_id']
  comentario <- dados[info$row,'comentario']
  records <-  data.frame(iris_id,comentario)
  
  observeEvent(input$salvar,{
    
    dbx::dbxUpdate(conn,"iris",records,where_cols = c("iris_id"))
  })
 
})


})
}

Chamando o app

shinyApp(ui, server)

conclusão

Procurei ser breve no tutorial mas, naturalmente, se você tiver alguma dúvida quanto aos passos, estou sempre à disposição: