I have a Shiny app that consists of two pages:
Page 1 displays a DataTable with summary information (ensembles).
Page 2 displays detailed pricing info (items) for a specific ensemble, which is selectable.
When the user clicks on a row on page 1, I want them to be taken to page 2, with the corresponding ensemble selected.
The below code creates the Shiny app and the two pages, but requires the user to switch pages and enter the ensemble number manually.
app.R
library(shiny)
## Create item pricing data
set.seed(1234)
init_items = function() {
item.id=1:1000
ensemble.id=rep(1:100,each=10)
cost=round(runif(1000,10,100), 2)
profit=round(cost*runif(1000,0.01,0.15), 2)
price=cost+profit
data.frame(item.id, ensemble.id, cost, price, profit)
}
items = init_items()
## Create ensemble pricing data
init_ensembles = function(items) {
items %>% group_by(ensemble.id) %>% summarize_each(funs(sum), cost, price, profit)
}
ensembles = init_ensembles(items)
## Attach dependencies
## https://github.com/timelyportfolio/functionplotR/issues/1#issuecomment-224369431
getdeps <- function() {
htmltools::attachDependencies(
htmltools::tagList(),
c(
htmlwidgets:::getDependency("datatables","DT")
)
)
}
# Define UI for application
ui <- shinyUI(
navbarPage("Linked Table Test",
tabPanel("Page 1", uiOutput("page1")),
tabPanel("Page 2", uiOutput("page2"), getdeps())
)
)
# Define server logic
server <- shinyServer(function(input, output, session) {
output$page1 <- renderUI({
inclRmd("./page1.Rmd")
})
output$page2 <- renderUI({
inclRmd("./page2.Rmd")
})
})
# Run the application
shinyApp(ui = ui, server = server)
page1.Rmd
# Ensembles
Click on an ensemble to display detailed pricing information.
```{r}
tags$div(
DT::renderDataTable(ensembles, rownames = FALSE)
)
```
page2.Rmd
# Items
```{r}
inputPanel(
numericInput("ensemble.id", label = "Ensemble ID:", 0, min(ensembles$ensemble.id), max(ensembles$ensemble.id))
)
tags$div(
renderText(paste0("Detailed pricing information for ensemble #",input$ensemble.id,":"))
)
tags$div(
DT::renderDataTable(items %>% filter(ensemble.id==input$ensemble.id) %>% select(-ensemble.id), rownames = FALSE)
)
```
This should give you the tools to do what you want:
library(shiny)
library(DT)
ui <- fluidPage(
tabsetPanel(
tabPanel("One",
DT::dataTableOutput("test1")
),
tabPanel("two",
numericInput("length","Length",0,0,10)
)))
server <- function(input, output, session) {
df <- reactive({
cbind(seq_len(nrow(mtcars)),mtcars)
})
output$test1 <- DT::renderDataTable({
df()
},rownames=FALSE,options=list(dom="t"),
callback=JS(
'table.on("click.dt", "tr", function() {
tabs = $(".tabbable .nav.nav-tabs li a");
var data=table.row(this).data();
document.getElementById("length").value=data[0];
Shiny.onInputChange("length",data[0]);
$(tabs[1]).click();
table.row(this).deselect();})'
))
}
shinyApp(ui = ui, server = server)
When you click a row in the datatable, it switches tabs, and changes the value of the numeric input to the value of the first column in the row you selected.
edit: you will probably have to put your datatables explicitly in the shiny app and not include them from a r markdown script, since I don't believe shiny objects in R Markdown have html Ids in a reliably readable way.
edit: I took your code and got it to work:
library(shiny)
library(dplyr)
## Create item pricing data
set.seed(1234)
init_items = function() {
item.id=1:1000
ensemble.id=rep(1:100,each=10)
cost=round(runif(1000,10,100), 2)
profit=round(cost*runif(1000,0.01,0.15), 2)
price=cost+profit
data.frame(item.id, ensemble.id, cost, price, profit)
}
items = init_items()
## Create ensemble pricing data
init_ensembles = function(items) {
items %>% group_by(ensemble.id) %>% summarize_each(funs(sum), cost, price, profit)
}
ensembles = init_ensembles(items)
## Attach dependencies
## https://github.com/timelyportfolio/functionplotR/issues/1#issuecomment-224369431
getdeps <- function() {
htmltools::attachDependencies(
htmltools::tagList(),
c(
htmlwidgets:::getDependency("datatables","DT")
)
)
}
# Define UI for application
ui <- shinyUI(fluidPage(
tabsetPanel(#id="Linked Table Test",
tabPanel("Page 1", DT::dataTableOutput("page1")),
tabPanel("Page 2", inputPanel(
numericInput("ensemble.id", label = "Ensemble ID:", 0, min(ensembles$ensemble.id), max(ensembles$ensemble.id))
),
textOutput("page2"), DT::dataTableOutput("table2"),getdeps())
)
))
# Define server logic
server <- shinyServer(function(input, output, session) {
output$page1 <- DT::renderDataTable(ensembles, rownames = FALSE,
callback=JS(
'table.on("click.dt", "tr", function() {
tabs = $(".tabbable .nav.nav-tabs li a");
var data=table.row(this).data();
document.getElementById("ensemble.id").value=data[0];
Shiny.onInputChange("ensemble.id",data[0]);
$(tabs[1]).click();
table.row(this).deselect();
})'
))
output$table2 <- DT::renderDataTable(items %>% filter(ensemble.id==input$ensemble.id) %>% select(-ensemble.id), rownames = FALSE)
output$page2 <- renderText({
print(input$ensemble.id)
paste0("Detailed pricing information for ensemble #",input$ensemble.id,":")
})
})
# Run the application
shinyApp(ui = ui, server = server)
Related
I am trying to implement an app for making a test. The app will relay on a random generation of data on which the questions are formulated and, in this particular case a single choice multiple selection question is implemented. There is an actionButton that triggers the generation of new questions and also an actionButton which evaluates the selected answer.
Here is the app:
library(pacman)
p_load(here)
p_load(tidyverse)
p_load(shiny)
p_load(plotly)
p_load(stringi)
##########################################################
### generate the questions with its evaluations
### a random sample of these will be used in the app
lore<-stri_rand_lipsum(1, start_lipsum = TRUE)
questions<-substring(lore, seq(1, nchar(lore), 25), seq(25, nchar(lore), 25)) %>% {.[1:10]}
dic<-data.frame(id=letters[1:10],quest=questions,out=sample(c(T,F),10,replace = T))
##########################################################
ui <- fluidPage(
titlePanel("exam test"),
sidebarLayout(
sidebarPanel(
actionButton("sim",label ="generate questions"),
uiOutput('resetable_input'),
actionButton("run",label ="evaluate")
),
mainPanel(
h3("you selected"),
textOutput("ans1"),
h3("correct?"),
textOutput("eval1"),
h3("the answer is"),
textOutput("sol1")
)
)
)
server <- function(input, output, session){
### build radioButtons based on a sample from dic df.
output$resetable_input <- renderUI({
times <- input$sim
temp_ind<- c( sample(which(dic$out),1),sample(which(!dic$out),3) )
temp_ind<-sample(temp_ind)
div(id=letters[(times %% length(letters)) + 1],
radioButtons("someb","Lorem ipsum dolor sit amet?",choiceNames=dic[temp_ind,"quest"],choiceValues=dic[temp_ind,"id"])
)
})
res_react<-eventReactive(
input$run,{
list(sel=dic[which(dic[,"id"]==input$someb),"quest"],
eval1=dic[which(dic[,"id"]==input$someb),"out"],
### here I don't know how to get the correct answer to display
sol="?")
}
)
output$ans1 <- renderText({ res_react()[["sel"]] })
output$eval1 <- renderText({ res_react()[["eval1"]] })
output$sol1 <- renderText({ res_react()[["sol"]] })
}
shinyApp(ui = ui, server = server)
The problem that I am facing is that I cannot get access to the whole set of available options from the radioButtons (id someb) in order to provide the correct answer in the last textOutput (output$sol1). I checked this out however I don't think this could be useful here since the available options must change each time the actionButton is activated.
Any advice is appreciated as always.
One option to achieve your desired result would be to use a reactiveVal to store the question data. To this end I first added a function generate_question. This function could first be used to init the reactiveVal when the app starts. Second, I added an observeEvent to generate a new question if the user requests so and updates the reactiveVal accordingly.
library(stringi)
library(shiny)
set.seed(123)
lore <- stri_rand_lipsum(1, start_lipsum = TRUE)
questions <- substring(lore, seq(1, nchar(lore), 25), seq(25, nchar(lore), 25))[1:10]
dic <- data.frame(id = letters[1:10], quest = questions, out = sample(c(T, F), 10, replace = T))
ui <- fluidPage(
titlePanel("exam test"),
sidebarLayout(
sidebarPanel(
actionButton("sim", label = "generate questions"),
uiOutput("resetable_input"),
actionButton("run", label = "evaluate")
),
mainPanel(
h3("you selected"),
textOutput("ans1"),
h3("correct?"),
textOutput("eval1"),
h3("the answer is"),
textOutput("sol1")
)
)
)
generate_question <- function() {
answers <- sample(c(sample(which(dic$out), 1), sample(which(!dic$out), 3)))
dic[answers, ]
}
server <- function(input, output, session) {
question <- reactiveVal(generate_question())
observeEvent(input$sim, {
question(generate_question())
})
output$resetable_input <- renderUI({
req(question())
div(
id = "quest",
radioButtons("someb", "Lorem ipsum dolor sit amet?",
choiceNames = question()[["quest"]],
choiceValues = question()[["id"]]
)
)
})
res_react <- eventReactive(
input$run,
{
list(
sel = question()[question()$id == input$someb, "quest"],
eval1 = question()[question()$id == input$someb, "out"],
sol = question()[question()$out, "quest"]
)
}
)
output$ans1 <- renderText({
res_react()[["sel"]]
})
output$eval1 <- renderText({
res_react()[["eval1"]]
})
output$sol1 <- renderText({
res_react()[["sol"]]
})
}
shinyApp(ui = ui, server = server)
so basically I am trying to develop a user interface where a person could select yes or no if they have the disease in the checkbox. Then it will take that yes/no answer for each corresponding disease and create a data frame so that a date table can be rendered and the person can see their responses. I've been struggling with trying to take what is clicked in the checkbox and putting it into a data frame with the diseases that I created another date frame for in the coding. I've tried several things and right now it keeps saying that the object 'data1', 'smoking', and 'diabetes' can't be found when I try to create the data frame from what the user would select. It seems my if/else statement is not working. Below is my code
library(shiny)
library(DT)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("IVD"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("Diabete", "Diabetes:",
choices = c("Yes" = "yes0",
"No" = "no0")),
checkboxGroupInput("Smoke", "Smoking:",
c("Yes" = "yes1",
"No" = "no1"))),
mainPanel(
fluidRow(actionButton("button", "Click for Risk Prediction")),
dataTableOutput("summary_table")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
observeEvent(input$button, {
output$summary_table<-renderDataTable({
a<- eventReactive(input$Diabete, {
if (input$Diabete == "yes0") {
a=1
} else {
a=0
}})
b<- eventReactive(input$Smoke, {
if (input$Diabete == "yes1") {
b=1
} else {
b=0
}})
ivd<-c('Diabetes','Smoking')
#data<- c(11,10,sugar,8,7,6,5,4,3,2,1)
values <- reactiveValues()
values$ivd <- data.frame()
eventReactive(input$Diabete, {
diabetes <- a
smoking <- b
da <- data.frame(diabetes, smoking)
data1 <- rbind(values$ivd, da)
})
ivd_data<-data.frame(ivd,data1,stringsAsFactors=FALSE )
print(ivd_data)
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
Try this
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("IVD"),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("Diabete", "Diabetes:",
choices = c("Yes" = "yes0",
"No" = "no0")),
checkboxGroupInput("Smoke", "Smoking:",
c("Yes" = "yes1",
"No" = "no1"))),
mainPanel(
fluidRow(actionButton("button", "Click for Risk Prediction")),
DTOutput("summary_table")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
observeEvent(input$button, {
a<- eventReactive(input$Diabete, {
a = ifelse(input$Diabete == "yes0",1,0)
})
b<- eventReactive(input$Smoke, {
b = ifelse(input$Smoke == "yes1",1,0)
})
ivd<- data.frame(a='Diabetes',b='Smoking')
data1 <- reactive({
data <- rbind(ivd,data.frame(a=a(),b=b()))
})
output$summary_table <- renderDT(data1())
})
}
# Run the application
shinyApp(ui = ui, server = server)
I want to creat an shiny app where users have to edit datatable.
There is the code contains reproductible exemple:
library(shiny)
library(dplyr)
library(DT)
line<-c(1,1,1,1,1)
op<-c(155,155,155,156,156)
batch<-c(1,2,3,1,2)
voile<-c(1,NA,NA,NA,NA)
depot<-c(2,NA,2,NA,NA)
boe<-data.frame(line,op,batch)
ui <- fluidPage(
# Application title
titlePanel("test dust"),
actionButton("refresh", label = "refresh"),
DT::dataTableOutput("mytable"),
actionButton("save", label = "save"),
)
# Define server logic required to draw a histogram
server <- function(input, output) {
DTdust<- eventReactive(input$refresh, {
DTdust <-data.frame(line,op,batch,voile,depot)
})
merged<-reactive({
merged<-merge(boe,DTdust(),all.x = TRUE)
})
mergedfiltred<-reactive({
mergedfiltred<- filter(merged(),is.na(voile)|is.na(depot) )
})
output$mytable = DT::renderDataTable( mergedfiltred(),editable = list(target = 'cell',
disable = list(columns = c(1:3))),selection = 'none'
)
}
# Run the application
shinyApp(ui = ui, server = server)
I wish this works like this —>
When user clic on refresh button. Dtdust.csv (here simulated) is read , then it merged with boe.csv (simulated too) an filter to get only rows without resulta for voile and depot col.
And display this merged filtred ino editable datatable .
This part works.
After i want to extract the data from edited datatable to make some processing on it (extract rows completed, rbind it on dtdust and save as dtdust.csv. But that’s ok i think.)
I’ m in trouble to extract edited datatable.
I see some exemple to do it with classic dataframe but it not work with reactive one.
I’m beeginner so if you can comment a lot your answers i can learn how to and not just ctrl+c ctrl+v your code :)
Thanks
You need to define a reactiveValues data frame. Then you need to update it via observeEvent whenever any cell is modified via mytable_cell_edit. The updated dataframe is now available in the server side, and part of it is now printed in the second table. You can use DF1$data for further analysis or subsetting. Full updated code is below.
library(shiny)
library(dplyr)
library(DT)
line<-c(1,1,1,1,1)
op<-c(155,155,155,156,156)
batch<-c(1,2,3,1,2)
voile<-c(1,NA,NA,NA,NA)
depot<-c(2,NA,2,NA,NA)
boe<-data.frame(line,op,batch)
ui <- fluidPage(
# Application title
titlePanel("test dust"),
actionButton("refresh", label = "refresh"),
DTOutput("mytable"), DTOutput("tb2"),
actionButton("save", label = "save"),
)
# Define server logic required to draw a histogram
server <- function(input, output) {
DF1 <- reactiveValues(data=NULL)
DTdust<- eventReactive(input$refresh, {
req(input$refresh)
DTdust <-data.frame(line,op,batch,voile,depot)
})
merged<-reactive({
req(DTdust())
merged<-merge(boe,DTdust(),all.x = TRUE)
})
mergedfiltred<-reactive({
mergedfiltred <- filter(merged(),is.na(voile)|is.na(depot) )
DF1$data <- mergedfiltred
mergedfiltred
})
output$mytable = renderDT(
mergedfiltred(),
editable = list(target = 'cell', disable = list(columns = c(1:3))), selection = 'none'
)
observeEvent(input$mytable_cell_edit, {
info = input$mytable_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
DF1$data[i, j] <<- DT::coerceValue(v, DF1$data[i, j])
})
output$tb2 <- renderDT({
df2 <- DF1$data[,2:5]
plen <- nrow(df2)
datatable(df2, class = 'cell-border stripe',
options = list(dom = 't', pageLength = plen, initComplete = JS(
"function(settings, json) {",
"$(this.api().table().header()).css({'background-color': '#000', 'color': '#fff'});",
"}")))
})
}
# Run the application
shinyApp(ui = ui, server = server)
Hi thanks for your solution #YBS.
I finaly find a solution by myself half an hour after asking here... (i previously turning arround hours and hours).
There is what i do :
output$x2 = DT::renderDataTable({
req(dat$x2)
DT::datatable(dat$x2)
})
dat <- reactiveValues()
# update edited data
observeEvent(input$mytable_cell_edit, {
data_table <- dat$x2
data_table[input$mytable_cell_edit$row, input$mytable_cell_edit$col] <- as.numeric(input$mytable_cell_edit$value)
dat$x2 <- data_table
})
Have a good day
Here is an example. The progress bar just jumps from 0% to 100% due a single function getres(). How to indicate the progress smoothly?
library("shiny")
library("shinyWidgets")
library("DESeq2")
library("airway")
data(airway)
getres <- function(eset){
dds<-DESeqDataSet(eset, design = ~cell + dex)
keep <- rowSums(counts(dds)) >= 10
dds <- dds[keep,]
dds <- DESeq(dds)
res <- results(dds)
return(res)
}
ui <- fluidPage(
tags$h1("Progress bar in Sweet Alert"),
useSweetAlert(), # /!\ needed with 'progressSweetAlert'
actionButton(
inputId = "go",
label = "Launch long calculation !"
)
)
server <- function(input, output, session) {
observeEvent(input$go, {
progressSweetAlert(
session = session, id = "myprogress",
title = "Work in progress",
display_pct = TRUE, value = 0
)
for (i in seq_len(1)) {
Sys.sleep(0.1)
updateProgressBar(
session = session,
id = "myprogress",
res<-getres(airway),
value = i
)
}
closeSweetAlert(session = session)
sendSweetAlert(
session = session,
title =" Calculation completed !",
type = "success"
)
})
}
shinyApp(ui = ui, server = server)
I was unable to run your example as airway and DESeq2 are not available for R 3.6+. BUT there is an interesting package that I have been meaning to try out called waiter.
Within waiter there is waitress which will "let you display loading bars on the entire screen or specific elements only."
There is a great demo app where you play with the different functions.
Here is an example from the docs!
library(shiny)
library(waiter)
ui <- navbarPage(
"Waitress on nav",
tabPanel(
"home",
use_waitress(),
plotOutput("plot")
)
)
server <- function(input, output){
# now waitress ranges from 0 to 100
waitress <- Waitress$new("nav", theme = "overlay", min = 0, max = 10)
output$plot <- renderPlot({
for(i in 1:10){
waitress$inc(1) # increase by 10%
Sys.sleep(.5)
}
hist(runif(100))
waitress$close() # hide when done
})
}
shinyApp(ui, server)
Hope this helps or gives you other ideas!
I am working on an application in sinydashboard in which the user generates a random number on the click of a button. The random number corresponds to a row in a dataframe which I need to display on the dashboard using an infoBox. Each infoBox needs to persist on the screen until the user closes the application.
I tried generating a new output variable on each click in server.R, however I could not find a way of referencing it in ui.R. Minimal example below. I've not included generating a name for an output variable on each button click as that's not working at all.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(dashboardHeader(title = ""),
dashboardSidebar(),
dashboardBody(fluidRow(
box(
width = 3,
actionButton(inputId = "generateButton",
label = "Generate")
),
box(infoBoxOutput("rnum1"))
)))
server <- function(input, output) {
randomData <- data.frame("RN" = runif(100, 1, 100))
observeEvent(input$generateButton, {
randomNumber <- floor(runif(1, 1, 100))
dataRow <- randomData[randomNumber,]
# Display dataRow in a persistent infoBox
# in a way that 5 clicks will produce 5 boxes
# Number of clicks is not known in advance
output$rnum1 <- renderInfoBox({
infoBox("Number", dataRow)
})
})
}
shinyApp(ui = ui, server = server)
Maybe this is what you want, at leat this a draft. You'll need a reactive variable to store the already generated numbers to be able to have something persistent.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(dashboardHeader(title = ""),
dashboardSidebar(),
dashboardBody(actionButton(inputId = "generateButton",
label = "Generate")
,
uiOutput('infoBoxes'))
)
server <- function(input, output) {
randomData <- data.frame("RN" = runif(100, 1, 100))
rv <- reactiveValues()
observeEvent(input$generateButton, {
randomNumber <- floor(runif(1, 1, 100))
dataRow <- randomData[randomNumber,]
print(dataRow)
rv$persistent <- c(rv$persistent, dataRow)
# Display dataRow in a persistent infoBox
})
output$infoBoxes = renderUI({
if(length(rv$persistent) > 0 ) {
fluidRow(
Map(function(x) infoBox('title', rv$persistent[x]), 1:length(rv$persistent))
)
}
})
}
shinyApp(ui = ui, server = server)