I have a Shiny Application that uses a MongoDB (using mongolite). The application loads and saves to the database with no issues but I am trying to find a way to edit the MongoDB through a datatable(using DT) where when the user edits or deletes a row that they can press an actionbutton to update the mongoDB. When I try to run it currently I am getting
"Warning: Error in : argument must be bson or json."
Is there a way for me to edit from DT, convert it to the JSON Mongo is expecting from the Shiny app? Below is the code.
library(shiny)
library(DT)
library(mongolite)
ui <- fluidPage(
# Application title
titlePanel("MongoTest"),
# Sidebar
sidebarLayout(
sidebarPanel(
actionButton("submit", "Submit"),
actionButton("load","Load"),
actionButton("update","update"),
actionButton("deleteRows", "Delete Rows")
),
#Main UI
mainPanel(
fluidPage(
fluidRow(h2("Interactive Table", align="center")),
tags$hr(),
fluidRow(
DT::dataTableOutput("Interactive_Table")
)
)
)
)
)
server = function(input, output, session){
#Function that loads the information from the mongoDB
loadData <- function() {
# Connect to the database
db = mongo(collection = "collectionhere",db ="SET", url = "mongodb://localhost:27017")
# Read all the entries
data <- db$find()
return(data)
}
READ_IN_DATA=loadData()
values <- reactiveValues(dfWorking = READ_IN_DATA)
#Function that saves data to DB
saveData <- function(data) {
# Connect to the database
db = mongo(collection = "collectionhere",db ="SET", url = "mongodb://localhost:27017")
data <- as.data.frame(t(data))
db$insert(data)
}
updateData = function(data){
# Connect to the database
db = mongo(collection = "collectionhere",db ="SET", url = "mongodb://localhost:27017")
data <- as.data.frame(t(data))
#subjects$update('{}', '{"$set":{"has_age": false}}', multiple = TRUE)
db$update(data)
}
#Loading In the Data
observeEvent(input$load, {
loadData()
})
#Update the DB based off changes to the table
observeEvent(input$update, {
updated_df=as.data.frame(values$dfWorking)
updateData(t(updated_df))
})
#Deleting Rows
observeEvent(input$deleteRows,{
if (!is.null(input$Interactive_Table_rows_selected)) {
values$dfWorking <- values$dfWorking[-as.numeric(input$Interactive_Table_rows_selected),]
}
})
#DT Table
output$Interactive_Table = renderDataTable({
datatable(values$dfWorking,editable=TRUE
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Related
In the following app, I would like to add a global button, to save the tables in the 2 panels at the same time.
Ideally, they should be saved to an xlsx file, in tabs named after the corresponding tabs.
Please note that the tabs were created using a module.
Many thanks!!
library(shiny)
library(DT)
modDtUi <- function(id){ # UI module
ns = NS(id)
DT::dataTableOutput(ns('x1'))
}
modDt <- function(input, output, session, data, globalSession){ # Server module
x <- data
output$x1 <- DT::renderDataTable(x, selection = 'none', editable = TRUE)
proxy <- dataTableProxy('x1', session = globalSession)
}
ui <- fluidPage(
mainPanel(
tabsetPanel(
tabPanel("Table1", modDtUi("editable")),
tabPanel("Table2", modDtUi("editable2"))
)
)
)
server <- function(input, output, session) {
callModule(modDt,"editable", data = head(iris,10), globalSession = session)
callModule(modDt,"editable2", data = tail(iris,5), globalSession = session)
}
shinyApp(ui = ui, server = server)
I believe this demo works.
I used reactiveValues v$data to store the data inside the module. The module will return v$data so it can be retrieved when you want to save the data in the shiny server.
I also added an observeEvent to detect changes in the data, and update the data table with replaceData.
The excel file is created using the writexl library, but you could substitute with others of course.
Let me know if this works for you. I imagine there are some elements of this answer that can be improved upon - and if we can identify them, would like to edit further.
library(shiny)
library(DT)
library(writexl)
modDtUi <- function(id){ # UI module
ns = NS(id)
DT::dataTableOutput(ns(id))
}
modDt <- function(input, output, session, data, id, globalSession){ # Server module
v <- reactiveValues(data = data)
output[[id]] <- DT::renderDataTable(v$data, selection = 'none', editable = TRUE)
proxy <- dataTableProxy(id, session = globalSession)
id_input = paste(id, "cell_edit", sep = "_")
# Could add observeEvent here to detect edit event
observeEvent(input[[id_input]], {
info = input[[id_input]]
if (!is.null(info)) {
v$data[info$row, info$col] <<- DT::coerceValue(info$value, v$data[info$row, info$col])
}
replaceData(proxy, v$data, resetPaging = FALSE)
})
return(data = reactive({v$data}))
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
width = 2,
actionButton("btn", "Save Both")
),
mainPanel(
tabsetPanel(
tabPanel("Table1", modDtUi("editable1")),
tabPanel("Table2", modDtUi("editable2"))
)
)
)
)
server <- function(input, output, session) {
e1 <- callModule(modDt, "editable1", data = head(iris,10), id = "editable1", globalSession = session)
e2 <- callModule(modDt, "editable2", data = tail(iris,5), id = "editable2", globalSession = session)
observeEvent(input$btn, {
print("Saving...")
sheets <- list("e1" = e1(), "e2" = e2())
write_xlsx(sheets, "test.xlsx")
})
}
shinyApp(ui = ui, server = server)
I wrote a Shiny app that loads several user-defined csv files as fileInput. The app is designed to plot data from a running measurement and new datapoints are written to the input files about every five minutes. I want to be able to reload all inputs by clicking on an actionButton.
I tried to define the function reading the .csv as eventReactive:
library(shiny)
ui <- fluidPage(
actionButton(inputId = "update", label = "Reload input files"),
fileInput(inputId = "file", label = "Choose file"),
textOutput("test")
)
server <- function(input, output) {
data <- eventReactive(input$update, {
mydata <- read.delim(input$file$datapath)
return(nrow(mydata))
})
output$test <- renderText(print(data()))
}
shinyApp(ui = ui, server = server)
When I choose an input file and click the action button, the output is correctly rendered. If I now open the csv file, add additional rows and click the action button again, the output is not updated.
Based on this answer I was able to create a workaround for you problem.
As I pointed out in my comment above, the reason why it is not possible to update fileInput with an action button is that, apparently, fileInput creates a temporary file in a temporary directory and the Input$file$datapth links to this temporary file. So you can reload the file with using the action button as often as you like, changes to the orignial file will not be reflected, since the link is pointing to the temporary file. I really don't know why inputFile works with temp files, but using the shinyFiles packages, you can build a workaround. You have one button which gets the real link to your file and load the data in and another button to reload the data. Pressing the load button will reload the original data and all changes to it will be reflected.
library(shiny)
library(shinyFiles)
ui <- fluidPage(
shinyFilesButton("GetFile", "Choose a file" ,
title = "Please select a file:", multiple = FALSE,
buttonType = "default", class = NULL),
actionButton(inputId = "reload", label = "Reload data"),
tableOutput("test")
)
server <- function(input,output,session){
volumes <- getVolumes()
v = reactiveValues(path = NULL)
observe({
shinyFileChoose(input, "GetFile", roots = volumes, session = session)
if (!is.null(input$GetFile)) {
file_selected <- parseFilePaths(volumes, input$GetFile)
v$path <- as.character(file_selected$datapath)
req(v$path)
v$data <- read.csv(v$path)
}
})
observeEvent(input$reload, {
req(v$path)
v$data <- read.csv(v$path)
})
output$test <- renderTable({
print(v$path)
if (is.null(v$data)) return()
v$data
})
}
shinyApp(ui = ui, server = server)
Update
It is also possible to combine this approach with reactiveFileReader, see example below:
library(shiny)
library(shinyFiles)
ui <- fluidPage(
shinyFilesButton("GetFile", "Choose a file" ,
title = "Please select a file:", multiple = FALSE,
buttonType = "default", class = NULL),
tableOutput("test")
)
server <- function(input,output,session){
volumes <- getVolumes()
v = reactiveValues(path = NULL)
observe({
shinyFileChoose(input, "GetFile", roots = volumes, session = session)
req(input$GetFile)
file_selected <- parseFilePaths(volumes, input$GetFile)
v$path <- as.character(file_selected$datapath)
req(v$path)
v$data <- reactiveFileReader(1000, session, filePath = v$path, readFun = read.csv, sep = ";")
})
output$test <- renderTable({
print(v$path)
req(v$data)
v$data()
})
}
shinyApp(ui = ui, server = server)
Trying to use selectInput in a form.
the choices are fetched from a collection in mongodB.
when user
completes the form and submits (which inserts to another collection
in mongo), data in selectInput is not captured.
tried to make it reactive or use observeEvent /updateSelectInput in the server but could not make it work.
here is the entire code:
library(shiny)
library(mongolite)
library(jsonlite)
# which fields get saved
fieldsAll <- c("Name", "selectOne", "tags")
saveData <- function(data) {
# Connect to the database
}
# load all responses into a data.frame
loadData <- function() {
# Connect to the database
}
fetchData <- function() {
# Connect to the database
}
shinyApp(
ui = tagList(
navbarPage(
tabPanel("Technology",
sidebarPanel(
textInput("Name",label ='Name:'),
selectInput('selectOne',
label ='Select One:',
choices=head(fetchData()),
selected = "",
multiple = FALSE),
selectizeInput("tags", "Tags:", NULL, multiple = TRUE, options=list(create=TRUE)),
actionButton("submit", "Submit", class = "btn-primary")
),
mainPanel(
tabsetPanel(
tabPanel("Table",
uiOutput("adminPanelContainer")
)
)
)
)
)
),
server = function(input, output, session) {
formData <- reactive({
fieldsAll
data <- sapply(fieldsAll, function(x) input[[x]])
data <- t(data)
data
})
observeEvent(input$submit, {
saveData(formData())
},
)
# render the admin panel
output$adminPanelContainer <- renderUI({
DT::dataTableOutput("responsesTable")
})
# Update the responses table whenever a new submission is made
responses_data <- reactive({
input$submit
data <- loadData()
data
})
# Show the responses in the admin table
output$responsesTable <- DT::renderDataTable({
DT::datatable(
responses_data(),
rownames = FALSE,
options = list(searching = TRUE, lengthChange = FALSE)
)
})
}
)
adding a column to the df with selected value worked:
formData <- reactive({
fieldsAll
data <- sapply(fieldsAll, function(x) input[[x]])
data <- c(data,selectOne= input$selectOne) #added line
data <- t(data)
data
})
I am new to R and i am using shiny package to build a shiny app that can take different type of queries and those queries takes dynamic user id provided by users on ui level and also i want to show the demo of result of query on ui.
So, my problem is that i am not able to store the query results into an data frame also on clicking on Download button csv is not storing in my system. my code is below. thanks.
###server
library(shiny)
library(RMySQL)
shinyServer(function(input, output) {
datasetInput <- reactive({
switch(input$queryset,
"CDR" = cdr,
"ASSET" = ast,
"USAGE" = usg)
})
output$tbl <- renderTable({
conn <- dbConnect(drv = RMySQL::MySQL(),dbname = "xxxx",
host = "xxxxxx",
username = "xxxxx",
password = "xxxxx"),
q<-dbSendQuery(conn,paste0("select * from table where user_id='",input$user_id,"' and start_time >= '2016-07-16' and start_time < '2016-07-28' order by start_time limit 10 ;",sep = ""
))
dat<- dbFetch(q,n=-1)
on.exit(dbDisconnect(conn), add = TRUE)
})
output$view <- renderTable({
head({dat}, n = input$nrows)
})
output$downloadData <- downloadHandler(
filename = function() { paste(input$user_id, '.csv', sep='') },
content = function(file) {
write.csv({dat}, file)
})
}
)
###ui
library(shiny)
shinyUI(fluidPage(
titlePanel("My App"),
sidebarLayout(
sidebarPanel(
selectInput("queryset", "Choose the type of query:",
choices = c("CDR", "ASSET", "USAGE")),
numericInput("nrows", "Enter the no. of observations:", 10),
numericInput("user_id", "Enter user_id:", 0),
downloadButton('downloadData', 'Download',class = NULL)
),
mainPanel(
tableOutput("view")
)
)
))
I am trying to do this autocomplete using a list which would be populated dynamically from a database everytime the applications loads. Unfortunately the following approach isn't working.
rm(list = ls())
library(shinysky)
library(shiny)
library(RMySQL)
loadData <- function(publisherId) {
# Connect to the database
mydb = dbConnect(MySQL(), user='root', password='root',host='localhost',dbname="tq")
# Construct the fetching query
query <- sprintf("select * from tq.publisher_dim where publisher_id = %s",publisherId) # Submit the fetch query and disconnect
data <- dbGetQuery(mydb, query)
dbDisconnect(mydb)
data
}
loadPubIds <- function() {
# Connect to the database
mydb = dbConnect(MySQL(), user='root', password='root', host='localhost', dbname="tq")
# Construct the fetching query
query <- sprintf("select distinct publisher_id from tq.publisher_dim" )
# Submit the fetch query and disconnect
data <- dbGetQuery(mydb, query)
dbDisconnect(mydb)
data
}
my_autocomplete_list <- c(loadPubIds())
ui <- fluidPage(
select2Input("txt","",choices = NULL,selected = NULL),
textInput(inputId ="publisherId", label="choose publisherId", value = "", width = NULL, placeholder = NULL),
actionButton("goButton", "Go!"),
dataTableOutput('mytable')
#textOutput('myquery')
)
server <- function(input, output,session) {
test <- reactive({
pubs <- loadPubIds()
})
observe({
pubs <- loadPubIds()
updateSelect2Input(session, 'txt', choices = as.list(pubs), label = "")
})
output$mytable <- renderDataTable({
if (input$goButton == 0)
return()
isolate({ loadData(input$publisherId) })
})
}
shinyApp(ui = ui, server = server)
Any help would be great.