Shiny: How to prevent selectInput from being reset by selecting the second selectInput
This builds on a question I ran into here , but I'm simplifying the example to make it easier to read (and therefore easier for future searchers).
reproducible example
Here is the data:
library(tidyverse)
library(shiny)
food_table <- structure(list(Food_category = c("Fruit", "Fruit", "Fruit", "Fruit",
"Vegetable", "Vegetable", "Fruit", "Fruit", "Fruit", "Vegetable"
), Food = c("Granny Smith apple", "McIntosh apple", "Avocado",
"Blueberries", "Broccoli, steamed", "Cucumber", "Banana", "Cherries",
"Nectarine", "Red onion"), Serving_Size = c(1, 1, 0.333333333333333,
1, 1, 1, 1, 1, 1, 1), Serving_Unit = c("apple", "apple", "avocado",
"cup", "cup", "cup", "banana", "cup", "nectarine", "cup"), Carbs_per_Serving = c(25,
25, 4, 21, 3.5, 4, 27, 25, 15, 11)), row.names = c(NA, -10L), class = c("tbl_df",
"tbl", "data.frame"))
(food_table)
Food_category Food Serving_Size Serving_Unit Carbs_per_Serving
<chr> <chr> <dbl> <chr> <dbl>
1 Fruit Granny Smith apple 1 apple 25
2 Fruit McIntosh apple 1 apple 25
3 Fruit Avocado 0.333 avocado 4
4 Fruit Blueberries 1 cup 21
5 Vegetable Broccoli, steamed 1 cup 3.5
6 Vegetable Cucumber 1 cup 4
7 Fruit Banana 1 banana 27
8 Fruit Cherries 1 cup 25
9 Fruit Nectarine 1 nectarine 15
10 Vegetable Red onion 1 cup 11
I have a shiny app which has multiple inputs. This works, except when a new selection is made in the second food category box, the food selection in the first row changes back to the first element. In other words, if I select "Fruit" in the first row, then "Avocado" and "1", then when I go to the second row and select "Vegetables", the selected food item changes from "Avocado" " becomes "Grandma "Smith Apple". I'm trying to figure out how to prevent the row from resetting after making a selection.
Here is the stripped down shiny code:
# Table filters
categories <- sort(unique(food_table$Food_category))
foods <- sort(unique(food_table$Food))
unit <- sort(unique(food_table$Serving_Unit))
# Create Shiny app
# Define UI
ui <- fluidPage(headerPanel(strong("Carbohydrate Calculator")),
fluidRow(
column(
width = 3,
selectInput(
inputId = 'cat_1',
label = 'Food Category',
choices = c("None", categories)
)),
column(
width = 3,
selectInput(
inputId = 'food_1',
label = 'Food Item',
choices = foods
)),
column(
width = 3,
numericInput(
inputId = "actual_serving_1",
label = "How much?",
value = "",
min = 0,
max = 100
))
),
fluidRow(
column(
width = 3,
selectInput(
inputId = 'cat_2',
label = 'Food Category',
choices = c("None", categories)
)),
column(
width = 3,
selectInput(
inputId = 'food_2',
label = 'Food Item',
choices = foods
)),
column(
width = 3,
numericInput(
inputId = "actual_serving_2",
label = "How much?",
value = "",
min = 0,
max = 100
))
),
column(8,
tableOutput("my_table"),
span(textOutput("my_message"), style="color:red")
) # Column close
) # fluidPage close
# Define server logic required to draw a histogram
server <- function(input, output, session) {
food_table_1 <- reactive({
food_table %>%
filter(Food_category == input$cat_1) %>%
filter(Food == input$food_1) %>%
mutate(Actual_amount = input$actual_serving_1)
})
food_table_2 <- reactive({
food_table %>%
filter(Food_category == input$cat_2) %>%
filter(Food == input$food_2) %>%
mutate(Actual_amount = input$actual_serving_2)
})
# Combine selections into a single table
combined_tables <- reactive({
do.call("rbind", list(food_table_1(),
food_table_2()
)
) %>%
mutate(Total_Carbs_grams = Carbs_per_Serving * Actual_amount / Serving_Size) %>%
select(Food_category, Food, Serving_Size, Serving_Unit, Actual_amount, Carbs_per_Serving, Total_Carbs_grams, everything())
})
# Render Output table
output$my_table <- renderTable({
combined_tables()
})
# Create observe function which updates the second selectInput when the first selectInput is changed
observe({
updateSelectInput(
session,
inputId = "food_1",
choices = food_table %>% filter(Food_category == input$cat_1) %>% pull(Food)
)
updateSelectInput(
session,
inputId = "food_2",
choices = food_table %>% filter(Food_category == input$cat_2) %>% pull(Food)
)
}) # Observe close
} # Server close
# Run the application
shinyApp(ui = ui, server = server)
You can set selected
in updateSelectInput
and input$food_1
respectively input$food_2
.
updateSelectInput(
session,
inputId = "food_1",
choices = food_table %>% filter(Food_category == input$cat_1) %>% pull(Food),
selected = input$food_1
)
Complete application code -
library(shiny)
library(dplyr)
categories <- sort(unique(food_table$Food_category))
foods <- sort(unique(food_table$Food))
unit <- sort(unique(food_table$Serving_Unit))
# Create Shiny app
# Define UI
ui <- fluidPage(headerPanel(strong("Carbohydrate Calculator")),
fluidRow(
column(
width = 3,
selectInput(
inputId = 'cat_1',
label = 'Food Category',
choices = c("None", categories)
)),
column(
width = 3,
selectInput(
inputId = 'food_1',
label = 'Food Item',
choices = foods
)),
column(
width = 3,
numericInput(
inputId = "actual_serving_1",
label = "How much?",
value = "",
min = 0,
max = 100
))
),
fluidRow(
column(
width = 3,
selectInput(
inputId = 'cat_2',
label = 'Food Category',
choices = c("None", categories)
)),
column(
width = 3,
selectInput(
inputId = 'food_2',
label = 'Food Item',
choices = foods
)),
column(
width = 3,
numericInput(
inputId = "actual_serving_2",
label = "How much?",
value = "",
min = 0,
max = 100
))
),
column(8,
tableOutput("my_table"),
span(textOutput("my_message"), style="color:red")
) # Column close
) # fluidPage close
# Define server logic required to draw a histogram
server <- function(input, output, session) {
food_table_1 <- reactive({
food_table %>%
filter(Food_category == input$cat_1) %>%
filter(Food == input$food_1) %>%
mutate(Actual_amount = input$actual_serving_1)
})
food_table_2 <- reactive({
food_table %>%
filter(Food_category == input$cat_2) %>%
filter(Food == input$food_2) %>%
mutate(Actual_amount = input$actual_serving_2)
})
# Combine selections into a single table
combined_tables <- reactive({
do.call("rbind", list(food_table_1(),
food_table_2()
)
) %>%
mutate(Total_Carbs_grams = Carbs_per_Serving * Actual_amount / Serving_Size) %>%
select(Food_category, Food, Serving_Size, Serving_Unit, Actual_amount, Carbs_per_Serving, Total_Carbs_grams, everything())
})
# Render Output table
output$my_table <- renderTable({
combined_tables()
})
# Create observe function which updates the second selectInput when the first selectInput is changed
observe({
updateSelectInput(
session,
inputId = "food_1",
choices = food_table %>% filter(Food_category == input$cat_1) %>% pull(Food),
selected = input$food_1
)
updateSelectInput(
session,
inputId = "food_2",
choices = food_table %>% filter(Food_category == input$cat_2) %>% pull(Food),
selected = input$food_2
)
}) # Observe close
} # Server close
# Run the application
shinyApp(ui = ui, server = server)