I have a Forecast table in Aurora, which I am able to read and filter-load using reactive. I am writing new rows to the table using an action button 'write_to_forecast_table', but I am not able to load the newly added row in the main table simultaneously.
tabPanel("Plant-Screen",
fluidRow(id='forecast_section',
column(width = 3, class='div-box-shadow',
tags$div(selectInput("fb_plant_input", label = 'Select Plant: ', choices= c(" All"), selected = NULL, multiple = FALSE, width = "100%"),
selectInput("fb_material_input", label = 'Commodity', choices = c(" All"), multiple=FALSE, width="100%"),
selectInput("fb_for_month_year", label = 'Procured for Month_Year', choices = c(" All"), multiple=FALSE, width="100%"),
br(),
tags$div(shinyWidgets::actionBttn(inputId = 'view_existing_forecasts', label='View Volume Forecast')),
tags$div(shinyWidgets::actionBttn(inputId = 'create_new_forecast', label='Create New Forecast')
),
bsModal(id="modal_demand_view",
title = "Create Volume Demand Forecast",
trigger = "create_new_forecast",
size="medium",
fluidRow(id = 'new_demand_form',
column(width = 12,
tags$div(style="display:contents;",
selectizeInput("selected_plant",
label = 'Plant',
options = list(placeholder = 'Select Plant'),
choices= unique(fb_plant_table$plant_name),
multiple=FALSE,
width="100%"),
br(),
selectizeInput("selected_material",
label = 'Commodity',
options = list(placeholder = 'Select Commodity'),
choices = unique(fb_material_table$commodity),
multiple=FALSE,
width="100%"),
br(),
airDatepickerInput("selected_month_year",
label = "Enter for Month-Year :",
value = lubridate::ymd(today()),
minDate = lubridate::ymd(today()) %m+% months(1),
maxDate = lubridate::ymd(today()) %m+% months(12),
view = "months",
minView = "months",
dateFormat = "yyyy-mm",
width = "50%",
autoClose = TRUE
),
br(),
numericInput("volume_requested",
label = "Enter Additional Volume",
value = 0,
width = "50%"),
br(),
selectizeInput("volume_unit", label = "Unit of Volume",
choices = c("pounds"),
multiple = FALSE,
width = "50%"),
br(),
selectInput("selected_supplier", label = "Supplier",
choices = "",
multiple = FALSE,
width = "50%")
)
)
),
br(),
tags$hr(),
tags$div(shinyWidgets::actionBttn(inputId = 'write_to_forecast_table',
label='New Volume Demand Created',
color="success",
style="material-flat")
)
)
)
),
column(width = 9, class='div-box-shadow',
tags$div(tags$h4 ('Volume Forecast Tabular View',
style="font-weight:bold;color: #ffd207;text-align: center;"
),
br(),
dataTableOutput("vol_forecast_meta_data") %>% shinycssloaders::withSpinner(color="#78620e")
)
Server Side:
#reading table from Aurora:
c <- dcon_iam()
vol_forecast_aurora <- DBI::dbGetQuery(c, 'select * from database_name.vol_forecast_aurora')
vol_forecast_aurora <- vol_forecast_aurora %>% group_by(plant_name, commodity, for_month_year) %>% mutate(cum_sum = cumsum(additional_volume))
DBI::dbDisconnect(c)
#Viewing forecast based on inputs selected from Select Input:
observeEvent(input$view_existing_forecasts, {
view_fc_reactive <- reactive({
vol_forecast_aurora %>%
filter(plant_name == input$fb_plant_input) %>%
filter(commodity == input$fb_material_input) %>%
filter(for_month_year == input$fb_for_month_year)
})
output$vol_forecast_meta_data <- DT::renderDataTable(view_fc_reactive(),
options = list(paging = FALSE, searching = FALSE),
rownames = FALSE)
})
#update table based on the new row added using Modal:
observeEvent(input$write_to_forecast_table, {
forecast_temp <- z$vol_forecast_aurora
forecast_temp$entered_by=input$user_id
forecast_temp$entered_on=lubridate::ymd(today())
forecast_temp$plant_name=input$selected_plant
forecast_temp$commodity=input$selected_material
forecast_temp$for_month_year=input$selected_month_year
forecast_temp$additional_volume=input$volume_requested
forecast_temp$unit_of_vol=input$volume_unit
forecast_temp$supplier=input$selected_supplier
forecast_temp = forecast_temp[, c('entered_by', 'entered_on', 'plant_name', 'commodity', 'for_month_year', 'additional_volume', 'unit_of_vol', 'supplier')]
c = dcon_iam()
write_to_caspian_aurora(c,
value= z$forecast_temp,
name="vol_forecast_aurora",
append = TRUE,
overwrite=FALSE,
row.names=FALSE
)
#removeModal('modal_demand_view')
showNotification({"Demand Forecast Submitted"})
DBI::dbDisconnect(c)
#reloading the data to the app:
c <- dcon_iam()
vol_forecast_aurora <- DBI::dbGetQuery(c, 'select * from spendanalytics_ico.vol_forecast_aurora')
vol_forecast_aurora <- vol_forecast_aurora %>% group_by(plant_name, commodity, for_month_year) %>% mutate(cum_sum = cumsum(additional_volume))
DBI::dbDisconnect(c)
})
I need help sorting the last part of the server: observeEvent(input$write_to_forecast_table, {})
Your problem is that the underlying data vol_forecast_aurora is only fetched once when the app is loaded (and that it is not reactive, so that the last lines in observeEvent(input$write_to_forecast_table only create a local object within the observer). Therefore, you don't see the changes when you update the DB within the app. I suggest that you store vol_forecast_aurora within a reactiveValues object, so that you can easily update it.
Untested code:
#reading table from Aurora:
c <- dcon_iam()
data <- reactiveValues(vol_forecast_aurora = DBI::dbGetQuery(c, 'select * from database_name.vol_forecast_aurora') %>% group_by(plant_name, commodity, for_month_year) %>% mutate(cum_sum = cumsum(additional_volume)))
DBI::dbDisconnect(c)
#Viewing forecast based on inputs selected from Select Input:
observeEvent(input$view_existing_forecasts, {
view_fc_reactive <- reactive({
data$vol_forecast_aurora %>%
filter(plant_name == input$fb_plant_input) %>%
filter(commodity == input$fb_material_input) %>%
filter(for_month_year == input$fb_for_month_year)
})
output$vol_forecast_meta_data <- DT::renderDataTable(view_fc_reactive(),
options = list(paging = FALSE, searching = FALSE),
rownames = FALSE)
})
#update table based on the new row added using Modal:
observeEvent(input$write_to_forecast_table, {
forecast_temp <- z$vol_forecast_aurora
forecast_temp$entered_by=input$user_id
forecast_temp$entered_on=lubridate::ymd(today())
forecast_temp$plant_name=input$selected_plant
forecast_temp$commodity=input$selected_material
forecast_temp$for_month_year=input$selected_month_year
forecast_temp$additional_volume=input$volume_requested
forecast_temp$unit_of_vol=input$volume_unit
forecast_temp$supplier=input$selected_supplier
forecast_temp = forecast_temp[, c('entered_by', 'entered_on', 'plant_name', 'commodity', 'for_month_year', 'additional_volume', 'unit_of_vol', 'supplier')]
c = dcon_iam()
write_to_caspian_aurora(c,
value= z$forecast_temp,
name="vol_forecast_aurora",
append = TRUE,
overwrite=FALSE,
row.names=FALSE
)
#removeModal('modal_demand_view')
showNotification({"Demand Forecast Submitted"})
DBI::dbDisconnect(c)
#reloading the data to the app:
c <- dcon_iam()
vol_forecast_aurora_local <- DBI::dbGetQuery(c, 'select * from spendanalytics_ico.vol_forecast_aurora')
data$vol_forecast_aurora <- vol_forecast_aurora_local %>% group_by(plant_name, commodity, for_month_year) %>% mutate(cum_sum = cumsum(additional_volume))
DBI::dbDisconnect(c)
})
Related
I have the shiny app below in which the user is able to select one or both dataframes and then based on the other filters to create a bar plot which will display the values of one or both dataframes. Now I have not connected it to the widgets in order to display what I want to achieve. It confuses me how to give the dataframes df1 and df2 to the plot_ly() and add_trace() functions together or one at a time.
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(dplyr)
library(plotly)
shinyApp(
ui = dashboardPagePlus(
header = dashboardHeaderPlus(title = "Social Media Metrics", titleWidth = 320
),
sidebar = dashboardSidebar(width = 320,
checkboxGroupInput("checkGroup", label = "Select Dataset",
choices = list("df1", "df2"),
selected = "df1"),
checkboxGroupInput("checkGroup2", label = "Select Social Network",
choices = list("FACEBOOK", "INSTAGRAM"),
selected = "FACEBOOK"),
radioButtons("radio", label = "Choose type of values",
choices = list("Absolute", "Percentages" ),
selected = "Absolute"),
uiOutput("x30"),
uiOutput("x16"),
uiOutput("value")
),
body = dashboardBody(
plotlyOutput("plot")
)
),
server = function(input, output) {
page<-c("ONE","TWO","THREE")
network<-c("INSTAGRAM","FACEBOOK","FACEBOOK")
av<-c(3.5,7.2,8.7)
growth<-c(5,7,9)
av2<-c(3.5,7.2,8.7)
growth2<-c(5,7,9)
df1<-data.frame(page,network,av,growth,av2,growth2)
page<-c("ONE","TWO","THREE")
network<-c("INSTAGRAM","FACEBOOK","FACEBOOK")
av<-c(4.5,7.9,8.7)
growth<-c(5,6,9)
av2<-c(3.5,9.2,8.7)
growth2<-c(5,43,9)
df2<-data.frame(page,network,av,growth,av2,growth2)
output$x30<-renderUI({
if("df1" %in% input$checkGroup){
new<-subset(df1, network %in% input$checkGroup2)
pickerInput(
inputId = "x3"#The colname of selected column
,
label = "Select Profile-df1" #The colname of selected column
,
choices = as.character(new$page)#all rows of selected column
,
multiple = TRUE,options = list(`actions-box` = TRUE)
)
}
else{
return(NULL)
}
})
output$x16<-renderUI({
if("df2" %in% input$checkGroup){
new<-subset(df2, network %in% input$checkGroup2)
pickerInput(
inputId = "x6"#The colname of selected column
,
label = "Select Profile-df2" #The colname of selected column
,
choices = as.character(new$page)#all rows of selected column
,
multiple = TRUE,options = list(`actions-box` = TRUE)
)
}
else{
return(NULL)
}
})
output$value<-renderUI({
if(input$radio=="Absolute"){
pickerInput(
inputId = "val"
,
label = "Select Absolut Value"
,
choices = c("growth","growth2")#all rows of selected column
,
multiple = F,options = list(`actions-box` = TRUE)
)
}
else{
pickerInput(
inputId = "val"
,
label = "Select Percentage Value"
,
choices = c("av","av2")#all rows of selected column
,
multiple = F,options = list(`actions-box` = TRUE)
)
}
})
output$plot<-renderPlotly({
fig <- plot_ly(df1, x = ~page, y = ~growth, type = 'bar', name = 'growth')
fig <- fig %>% add_trace(df2,y = ~growth, name = 'growth')
fig <- fig %>% layout(yaxis = list(title = 'Count'), barmode = 'group')
fig
})
}
)
A reactive dataframe dfa() gives the selected data from either or both dataframes. Now, plotly requires some work. Please note that working with variables within dfa() gives some errors as both df1 and df2 are defined within the server and are available in output$plot, and hence, you may need to use dfa()$id, etc.
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(dplyr)
library(plotly)
shinyApp(
ui = dashboardPagePlus(
header = dashboardHeaderPlus(title = "Social Media Metrics", titleWidth = 320
),
sidebar = dashboardSidebar(width = 320,
checkboxGroupInput("checkGroup", label = "Select Dataset",
choices = list("df1", "df2"),
selected = "df1"),
checkboxGroupInput("checkGroup2", label = "Select Social Network",
choices = list("FACEBOOK", "INSTAGRAM"),
selected = "FACEBOOK"),
radioButtons("radio", label = "Choose type of values",
choices = list("Absolute", "Percentages" ),
selected = "Absolute"),
uiOutput("x30"),
uiOutput("x16"),
uiOutput("value")
),
body = dashboardBody(
plotlyOutput("plot") , DTOutput("tb1")
)
),
server = function(input, output) {
page<-c("ONE","TWO","THREE")
network<-c("INSTAGRAM","FACEBOOK","FACEBOOK")
av<-c(3.5,4.2,8.7)
growth<-c(4,7,9)
av2<-c(3.5,7.2,4.7)
growth2<-c(4,7,9)
id <- rep("df1",3)
df1<-data.frame(page,network,av,growth,av2,growth2,id)
page<-c("ONE","TWO","THREE")
network<-c("INSTAGRAM","FACEBOOK","FACEBOOK")
av<-c(4.5,7.9,8.7)
growth<-c(5,4,8)
av2<-c(3.5,9.2,6.7)
growth2<-c(5,4,9)
id <- rep("df2",3)
df2<-data.frame(page,network,av,growth,av2,growth2,id)
output$x30<-renderUI({
if (is.null(input$checkGroup)) {
return(NULL)
}else if("df1" %in% input$checkGroup){
new<-subset(df1, network %in% input$checkGroup2)
pickerInput(
inputId = "x3" #The rownames of selected column
,
label = "Select Profile-df1" #The colname of selected column
,
choices = as.character(new$page) #all rows of selected column
,
multiple = TRUE,options = list(`actions-box` = TRUE)
)
}
else{
return(NULL)
}
})
output$x16<-renderUI({
if (is.null(input$checkGroup)) {
return(NULL)
}else if( "df2" %in% input$checkGroup){
new2<-subset(df2, network %in% input$checkGroup2)
pickerInput(
inputId = "x6" #The rownames of selected column
,
label = "Select Profile-df2" #The colname of selected column
,
choices = as.character(new2$page) #all rows of selected column
,
multiple = TRUE,options = list(`actions-box` = TRUE)
)
}
else{
return(NULL)
}
})
output$value<-renderUI({
if(req(input$radio)=="Absolute"){
pickerInput(
inputId = "val"
,
label = "Select Absolut Value"
,
choices = c("growth","growth2") #all rows of selected column
,
multiple = F, options = list(`actions-box` = TRUE)
)
}else if(req(input$radio)=="Percentages"){
pickerInput(
inputId = "val"
,
label = "Select Percentage Value"
,
choices = c("av","av2")#all rows of selected column
,
multiple = F,options = list(`actions-box` = TRUE)
)
} else {return(NUL)}
})
dfa <- reactive({
req(input$val)
if (is.null(input$checkGroup)) {return(NULL)}
if (input$checkGroup == "df1" | length(input$checkGroup) == 2){
df11 <- df1 %>% filter(page %in% req(input$x3) & network %in% req(input$checkGroup2))
}else df11 <- NULL
if (input$checkGroup == "df2" | length(input$checkGroup) == 2){
df22 <- df2 %>% filter(page %in% req(input$x6) & network %in% req(input$checkGroup2))
}else df22 <- NULL
if (is.null(df11) & is.null(df22)) {return(NULL)
}else {
if (is.null(df11)){df <- df22
}else if (is.null(df22)){df <- df11
}else { df <- rbind(df11,df22) }
df <- df %>% transform(y=df[[as.name(input$val)]], x=page)
}
df
})
output$tb1 <- renderDT(dfa())
output$plot<-renderPlotly({
if (is.null(dfa())) return(NULL)
xvar <- unique(dfa()$page)
xform <- list(categoryorder = "array",
categoryarray = xvar) ## to get the bars in the order you wish to display
fig <- plot_ly()
fig <- fig %>% add_trace( dfa() , x = ~x, y = ~y, type='bar', name='growth', fill=x, color=dfa()$id)
fig <- fig %>% layout(xaxis = list(xform, title="Page"),
yaxis = list(title = 'Count'), barmode = 'group')
fig
})
}
)
I have tried creating a drop-down that depends on a different widget ID.
In this app, the symbol drop-down-list depends on the input from the Stock Class chckboxGroupButtons()
I've successfully implemented that, but after inserting an eventReactive () that delays the reactive filtering and only starts filtering once "apply" is selected, the app shows an empty dataframe to start with.
Only AFTER clicking on either "Apply" or "Reset" then the app works the way it should.
I just need the unfiltered dataframe when rendering the app for the first time.
How do I fix this?
Code:
---
title: "Sample App"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
library(flexdashboard)
library(shiny)
library(shinyWidgets)
library(shinyjs)
# Core
library(tidyverse)
library(lubridate)
# Make data
symbols <- purrr::cross_df(list(numbers = 1:90,letters = LETTERS))
symbols$symbol <- paste(symbols$numbers,symbols$letters,sep = " - ")
set.seed(123)
stock_prices_tbl <- tibble(
date = seq.Date(from = ymd("2018-01-01"), to = Sys.Date(), length.out = 90),
class = sample(c("marketing", "sales", "research"), size = 90, replace = TRUE),
symbol = sample(symbols$symbol,size = 90,replace = F),
adjusted = runif(n = 90,min = 0,90)
)
Sidebar {.sidebar}
---
# Allow shiny js
shinyjs::useShinyjs(rmd = T)
dateRangeInput(
inputId = "date_range",
label = h4("Date Range"),
start = min(stock_prices_tbl$date),
end = max(stock_prices_tbl$date),
min = min(stock_prices_tbl$date),
max = max(stock_prices_tbl$date),
startview = "month")
shinyWidgets::checkboxGroupButtons(
inputId = "class",
label = h4("Stock Class"),
choices = unique(stock_prices_tbl$class),
selected = unique(stock_prices_tbl$class),
checkIcon = list(
yes = icon("ok", lib = "glyphicon"),
no = icon("remove", lib = "glyphicon")
))
shiny::renderUI({
shinyWidgets::pickerInput(
inputId = "symbol",label = h4("Symbol"),
choices = unique(stock_prices_tbl[stock_prices_tbl$class %in% input$class,]$symbol),
selected = unique(stock_prices_tbl[stock_prices_tbl$class %in% input$class,]$symbol),
multiple = TRUE,
options = list(
`actions-box` = TRUE,
size = 10,
`selected-text-format` = "count",
`live-search` = TRUE
))
})
br()
hr()
br()
shiny::actionButton(inputId = "apply",label = "Apply",icon = shiny::icon("play"))
# Resetting app to default values:
actionButton(inputId = "reset", label = "Reset", icon = shiny::icon("sync"))
observeEvent(eventExpr = input$reset, handlerExpr = {
updateCheckboxGroupButtons(
session = session,
inputId = "class",
selected = unique(stock_prices_tbl$class))
updatePickerInput(
session = session,
inputId = "symbol",
selected = unique(stock_prices_tbl$symbol))
updateDateRangeInput(
session = session,
inputId = "date_range",
start = min(stock_prices_tbl$date),
end = max(stock_prices_tbl$date))
# We'll need to mimic a click when resetting our app to defaults
shinyjs::delay(ms = 300,expr = {shinyjs::click(
id = "apply" # The input id that you want to enforce when resetting to defaults
)
})
})
# Delay Reactions
stocks_reactive <- shiny::eventReactive(eventExpr = input$apply ,valueExpr = {
stock_prices_tbl %>%
filter(
between(date,input$date_range[1],input$date_range[2]) &
class %in% input$class &
symbol %in% input$symbol
)
},ignoreNULL = F, ignoreInit = F)
Column {data-width=1000}
---
renderPrint(expr = {stocks_reactive()})
I have added multiple select inputs to my shiny app in the sidebar and in the main body and want to create a graph that will change when any of those inputs have been selected or changed but I keep getting the error Warning: Error in : Result must have length 56127, not 0.
UI:
ui <- dashboardPage(
dashboardHeader(title = "Human Trafficking"),
dashboardSidebar(
sidebarMenu(
selectInput("Source", "Choose a Data Source: ", choices = " ", selected = NULL,
multiple = TRUE, selectize = TRUE, width = NULL, size = NULL),
dateInput("startdate", "Start Date:", value = "2009-01-01", format = "dd-mm-yyyy",
min = "2009-01-01", max = "2019-08-26"),
dateInput("enddate", "End Date:", value = "2019-08-27", format = "dd-mm-yyyy",
min = "2009-01-02", max = "2019-08-27"),
selectInput("Nationality", "Select a nation: ", choices = " "),
actionButton("button", "Apply")
)
),
dashboardBody(
fluidRow(
box(width = 4, solidHeader = TRUE,
selectInput("traffickingType", "Choose a trafficking type: ", choices = " ", selected = NULL,
multiple = TRUE, selectize = TRUE, width = NULL, size = NULL)
),
box(width = 4, solidHeader = TRUE,
selectInput("traffickingSubType", "Choose a trafficking sub type: ", choices = " ", selected = NULL,
multiple = TRUE, selectize = TRUE, width = NULL, size = NULL)
),
box(width = 4, solidHeader = TRUE,
selectInput("gender", "Choose a gender: ", choices = " ", selected = NULL,
multiple = TRUE, selectize = TRUE, width = NULL, size = NULL)
)
),
fluidRow(
box(width = 12,
plotlyOutput('coolplot')
)
)
)
)
Server:
server <- function(input, output, session) {
genderVic = sort(unique(ngo$Victim.Gender))
updateSelectInput(session, "gender", choices = genderVic)
traffickingSub = sort(unique(ngo$Trafficking.Sub.Type))
updateSelectInput(session, "traffickingSubType", choices = traffickingSub)
trafficking = sort(unique(ngo$Trafficking.Type))
updateSelectInput(session, "traffickingType", choices = trafficking)
traffickerNationalities = sort(unique(ngo$Trafficker.Nationality))
updateSelectInput(session, "TraffickerNation", choices = traffickerNationalities)
dataSource = sort(unique(ngo$Data.Provided.By))
updateSelectInput(session, "Source", choices = dataSource)
nationalities = sort(unique(ngo$Victim.Nationality))
updateSelectInput(session, "Nationality", choices = nationalities)
output$coolplot <- renderPlotly({
ngo <-
ngo %>%
filter(Victim.Nationality == input$Nationality,
Victim.Gender == input$gender,
Trafficking.Type == input$traffickingType
)
p = ggplot(ngo, aes(x = Victim.Age, fill = Trafficking.Type)) +
geom_bar(position = "stack")
ggplotly(p) %>%
layout(showlegend = FALSE)
})
}
So currently only have it calling three of the inputs to test it but still getting an error.
It should work after the error is displayed once you select a gender and trafficking type in your example. The reason for the error is that renderPlotly is expecting values for input$traffickingType and input$gender but these start out as NULL.
Add a req for each of those selectInputs:
output$coolplot <- renderPlotly({
ngo <-
ngo %>%
filter(Victim.Nationality == input$Nationality,
Victim.Gender == req(input$gender),
Trafficking.Type == req(input$traffickingType)
)
p = ggplot(ngo, aes(x = Victim.Age, fill = Trafficking.Type)) +
geom_bar(position = "stack")
ggplotly(p) %>%
layout(showlegend = FALSE)
})
I'm building a R Shiny app with a dynamic datatable, using the DT package. Users are able to select two columns within a data.frame that contains more columns.
When users select a column, the datatable is updated and all filters/sorting are reset to default within the datatable object. How can I let the application remember filters and sorting when the given column is not replaced by the user?
Minimal working example below:
library(shiny)
library(DT)
library(data.table)
server <- function(input, output) {
df <- data.frame(
name = rep('a',20),
dimA = 1:20,
dimB = 21:40,
dimC = 41:60
)
observe({
columns <- c('name', input$dim1ID, input$dim2ID)
dfDt <- df[names(df) %in% columns]
output$dtDataTable = DT::renderDataTable(
server = FALSE,
expr = datatable(
dfDt,
filter = 'top',
rownames = FALSE,
selection = 'none',
options = list(sDom = '<"top">rt<"bottom">ip')
)
)
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
## Dimension 1
selectInput(
inputId = "dim1ID",
label = "Dimensie 1",
choices = c('dimA', 'dimB', 'dimC'),
selected = 'dimA'
),
## Dimension 2
selectInput(
inputId = "dim2ID",
label = "Dimensie 2",
choices = c('dimA', 'dimB', 'dimC'),
selected = 'dimB'
)
),
mainPanel(DT::dataTableOutput('dtDataTable'))
)
)
shinyApp(ui = ui, server = server)
This can be done using the DataTables Information, in particular the "state" information (input$tableId_state) which contains the order information of the current table, and input$tableId_search_columns which contains the filtering information by columns. If the columns are fixed (ie in the example above "Dimensie 1" and "Dimensie 2" would always be at the same place), it is much simpler to "remember" which one was ordered (unlike the original example where they are alphabetically reordered when the table is created). For instance based on the above example, the following will work if you sort the "A" column and change the right column from "B" to "C" and back:
library(shiny)
library(DT)
library(data.table)
server <- function(input, output) {
df <- data.frame(
name = rep('a',20),
dimA = 1:20,
dimB = 21:40,
dimC = 41:60
)
values <- reactiveValues(
prevDim1 = "",
prevDim2 = "",
options = list(sDom = '<"top">rt<"bottom">ip',
stateSave = TRUE,
order = list())
)
observeEvent(input$dtDataTable_state$order, {
values$options$order <- input$dtDataTable_state$order
})
observeEvent({
input$dim1ID
input$dim2ID
},{
columns <- c('name', input$dim1ID, input$dim2ID)
dfDt <- df[names(df) %in% columns]
if(length(values$options$order) != 0 && ((values$prevDim1 != input$dim1ID && values$options$order[[1]][[1]] == 1) | (values$prevDim2 != input$dim2ID && values$options$order[[1]][[1]] == 2)) ){
values$options$order = list()
}
values$prevDim1 <- input$dim1ID
values$prevDim2 <- input$dim2ID
output$dtDataTable = DT::renderDataTable(
server = FALSE,
expr = datatable(
dfDt,
filter = 'top',
rownames = FALSE,
selection = 'none',
options = values$options
)
)
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
## Dimension 1
selectInput(
inputId = "dim1ID",
label = "Dimensie 1",
choices = c('dimA', 'dimB', 'dimC'),
selected = 'dimA'
),
## Dimension 2
selectInput(
inputId = "dim2ID",
label = "Dimensie 2",
choices = c('dimA', 'dimB', 'dimC'),
selected = 'dimB'
)
),
mainPanel(DT::dataTableOutput('dtDataTable'))
)
)
shinyApp(ui = ui, server = server)
In shiny, I use plotOutput to output a table, and I want to highlight some cells of it according to some criteria.
Is there any functions in shiny that could achieve this?
Thank you in advance!
======================
Besides to highlighting, I'd also like to add radio buttons on the left of the table, so I could know which lines user chose. Now I'm using renderDataTable to do this, however it doesn't seem to have the highlighting function.
Could it be possible?
Hello a solution without ggplot2 but with package ReporteRs, see the app below for example, the main function is FlexTable :
EDIT : yes, you can put shiny widgets into the HTML table, here an example with checkboxInput for selecting rows :
library(ReporteRs)
library(shiny)
mtcars = mtcars[1:6, ]
runApp(list(
ui = pageWithSidebar(
headerPanel = headerPanel("FlexTable"),
sidebarPanel = sidebarPanel(
selectInput(inputId = "colCol", label = "Col to color", choices = c("None", colnames(mtcars)), selected = "None"),
selectizeInput(inputId = "rowCol", label = "Row to color", choices = rownames(mtcars), multiple = TRUE,
options = list(placeholder = 'None', onInitialize = I('function() { this.setValue(""); }')))
),
mainPanel = mainPanel(
uiOutput(outputId = "tableau"),
br(),
verbatimTextOutput(outputId = "row_select"),
uiOutput(outputId = "car_selected")
)
),
server = function(input, output, session) {
output$tableau <- renderUI({
# here we add check box into the table: it create 6 new input widgets
mtcars$choice = unlist(lapply(1:nrow(mtcars),
FUN = function(x) { paste(capture.output(checkboxInput(inputId = paste0("row", x),
label = paste("Row", x),
value = TRUE)), collapse = " ") }))
tabl = FlexTable( mtcars,
# tune the header and the cells
header.cell.props = cellProperties( background.color = "#003366", padding = 5 ),
body.cell.props = cellProperties( padding = 5 ),
header.text.props = textBold( color = "white" ),
add.rownames = TRUE )
tabl = setZebraStyle( tabl, odd = "#DDDDDD", even = "#FFFFFF" )
# set a column's color
if (input$colCol != "None") {
tabl = setColumnsColors( tabl, j=which(names(mtcars) %in% input$colCol ), colors = "orange" )
}
# set a row's color
if (!is.null(input$rowCol)) {
tabl = setRowsColors( tabl, i=which(rownames(mtcars) %in% input$rowCol ), colors = "#3ADF00" )
}
return(HTML(as.html(tabl)))
})
output$row_select <- renderPrint({
# you can use the input created into the table like others
c("row1" = input$row1, "row2" = input$row2, "row3" = input$row3, "row4" = input$row4, "row5" = input$row5, "row6" = input$row6)
})
output$car_selected <- renderUI({
# if you have more than 6 rows it could be convenient
selected = eval(parse(text = paste("c(", paste(paste0("input$row", 1:6), collapse =", "), ")")))
HTML(paste0("You have selected the following cars : ", paste(rownames(mtcars)[selected], collapse = ", ")))
})
}
))
Which render like this (with check box) :