从 DT::renderDT 调用时,R shiny 反应值不会重新计算
R shiny reactive value does not recalculate when called from DT::renderDT
我闪亮的应用程序中的反应值在第一次计算后从 DT::renderDT 函数内部调用时不会重新计算。
这是我的代码:
#-------------------------------------------------------------------------------------------------
# ENVIRONMENT & PACKAGES
#-------------------------------------------------------------------------------------------------
# Set working directory
setwd('C:/Users/username/OneDrive/Desktop/Coding projects/Paleo Diet Planner')
# Clear workspace
rm(list = ls())
# Package/library list
pckgs <- c('shiny','shinydashboard','reactlog','DT','dplyr','stringr','mgsub')
# Install and load libraries
for(pckg in pckgs)
{
if(!(pckg %in% rownames(installed.packages()))) install.packages(pckg)
if(!(pckg %in% (.packages()))) library(pckg, character.only = TRUE)
}
# Remove unnecessary variables
rm(pckg,pckgs)
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# DATA
#-------------------------------------------------------------------------------------------------
# Static Data
Recipe_Inv <- readRDS('./Data/Recipe_Inv.rds')
# Get unique tags
Recipe_Tags <- Recipe_Inv$Tags %>% stringr::str_split(., ';', simplify = T) %>%
trimws %>% as.vector %>% unique %>% magrittr::extract(. != '') %>% magrittr::extract(order(.))
# Utility variables
Debug_Flag <- F
Log_Flag <- T
LogFile <- c()
LogFile_Path <- as.character(Sys.time()) %>% mgsub(., c('-', ' ', ':'), c('', '_', '-')) %>% paste0('./Logs/',.,'.txt')
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# UTILITY FUNCTIONS
#-------------------------------------------------------------------------------------------------
# Function for checking if the tags chosen for filtering are present in the tag string, i.e.
# string with tags separated by semi-colon
Filter_Tags <- function(Tags, Tag_Crit){
# Debug and log
# if(Log_Flag) print('Utility_Function -> Filter_Tags')
if(Debug_Flag) browser()
#Extract tags from the tag string as character vector
Tag_Ls <- trimws(stringr::str_split(Tags, ';')[[1]])
# Check if the intersection (common elements) of the tag string and filter tags vector have the same length
# Equivalent to all filter tags being present in the tag string
length(intersect(Tag_Ls, Tag_Crit)) == length(Tag_Crit)
}
# Function to filter out, rearrange and order the tags when select drop-down field is used
ReArrange_Tags <- function(Tags, Tag_Crit){
# Log
# if(Log_Flag) print('Utility_Function - > ReArrange_Tags')
if(is.null(Tag_Crit)){
# If the select drop-down list is empty, leave tag string (tags delimited by semi-colon) as-is
Tags
}else{
# Debug
if(Debug_Flag) browser()
# Get the tags in the tag string that were chosen in the select drop-down field
# and order them aplhabetically
trimws(stringr::str_split(Tags, ';')[[1]]) %>%
intersect(., Tag_Crit) %>% magrittr::extract(order(.)) %>%
paste0(., collapse = ';')
}
}
# Function for logging events in the apps in terms of UI element usage and server activity
Log_App_Activity <- function(print_txt){
print(print_txt)
LogFile <<- c(LogFile,print_txt)
write.table(LogFile, LogFile_Path, sep = '\n', col.names = F, row.names = F)
}
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# USER INTERFACE
#-------------------------------------------------------------------------------------------------
#### Separate components ####
# Dashboard Header
db_header <- shinydashboard::dashboardHeader(
# Dashboard title
title = 'Paleo Diet Planner'
)
# Dashboard Sidebar
db_Sidebar <- shinydashboard::dashboardSidebar(
#### Sidebar settings ####
# ID of the dashboard sidebar object
id = 'InSidebar_Menu',
# Width setting
width = 350,
#### UI elements ####
#
shiny::selectInput(inputId = 'InSelect_RecipeTags',
label = 'Select Recipe Categories',
choices = Recipe_Tags, multiple = T),
#
shinydashboard::menuItem(text = 'Recipe List', tabName = 'tbRecipeLs',
icon = shiny::icon('th-list')),
#
shinydashboard::menuItem(text = 'Recipe View', tabName = 'tbRecipeView',
icon = shiny::icon('readme'))
)
# Dashboard Body
db_Body <- shinydashboard::dashboardBody(
#
shinydashboard::tabItems(
#
shinydashboard::tabItem(
#
tabName = 'tbRecipeLs',
#
DT::DTOutput(outputId = 'OutDT_RecipeList')
),
#
shinydashboard::tabItem(
#
tabName = 'tbRecipeView',
#
shiny::uiOutput('OutUI_RecipeURL'),
#
shiny::htmlOutput("OutUI_RecipeWebsite")
)
)
)
#### Main UI ####
Main_UI <- dashboardPage(db_header, db_Sidebar, db_Body)
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# SERVER
#-------------------------------------------------------------------------------------------------
# Define server logic
server <- function(input, output){
# Filtered Recipe Inventory based on the select drop-down field 'InSelect_RecipeTags'
Recipe_Inv_Flt <- shiny::reactive({
# Debug and log
if(Log_Flag) Log_App_Activity('shiny::reactive -> Recipe_Inv_Flt')
if(Debug_Flag) browser()
# Check if the select drop-down field 'InSelect_RecipeTags' has been used and filter appropriately
if(is.null(input$InSelect_Recipe_Tags)){
Recipe_Inv
}else{
Recipe_Inv %>% dplyr::rowwise() %>%
dplyr::filter(Filter_Tags(Tags, input$InSelect_RecipeTags)) %>%
dplyr::ungroup()
}
})
# Data Table displaying the reactive values Recipe_Inv_Flt() with appropriate tags displayed
# in the tag column, based on the tags selected in the select drop-down field 'InSelect_RecipeTags'
output$OutDT_RecipeList <- DT::renderDT({
# Debug and log
if(Log_Flag) Log_App_Activity('DT::renderDT -> OutDT_RecipeList')
if(Debug_Flag) browser()
#
Recipe_Inv_Flt() %>% dplyr::select(Tags, Recipe_Nm) %>% dplyr::rowwise() %>%
dplyr::mutate(Tags = ReArrange_Tags(Tags, input$InSelect_RecipeTags)) %>%
dplyr::ungroup() %>% dplyr::arrange(Tags)
})
#
output$OutUI_RecipeURL <- shiny::renderUI({
# Debug and log
if(Log_Flag) Log_App_Activity('shiny::renderUI -> OutUI_RecipeURL')
if(Debug_Flag) browser(text = 'shiny::renderUI -> OutUI_RecipeURL')
#
choices <- Recipe_Inv_Flt() %>% .$Recipe_Nm %>% magrittr::extract(input$OutDT_RecipeList_rows_selected)
#
shiny::selectInput(inputId = 'InSelect_RecipeURL',
label = 'Select recipe to display',
choices = choices, multiple = F)
})
#
output$OutUI_RecipeWebsite <- shiny::renderUI({
#Debug and log
if(Log_Flag) Log_App_Activity('shiny::renderUI -> OutUI_RecipeWebsite')
if(Debug_Flag) browser(text = 'shiny::renderUI -> OutUI_RecipeWebsite')
#
if(!is.null(input$InSelect_RecipeURL)){
Recipe_URL <- Recipe_Inv %>% dplyr::filter(Recipe_Nm == input$InSelect_RecipeURL) %>% .$Recipe_URL
}else{
Recipe_URL <- NA
}
#
shiny::tags$iframe(src = Recipe_URL, width = "100%", height = 800)
})
# Log for UI elements
shiny::observeEvent(input$InSelect_RecipeTags,{
browser()
if(Log_Flag) Log_App_Activity('shiny::selectInput -> InSelect_RecipeTags')
})
shiny::observeEvent(input$InSidebar_Menu,{
browser()
if(Log_Flag){
switch(input$InSidebar_Menu,
'tbRecipeLs' = Log_App_Activity('shinydashboard::menuIte -> tbRecipeLs'),
'tbRecipeView' = Log_App_Activity('shinydashboard::menuIte -> tbRecipeView'))
}
})
shiny::observeEvent(input$InSelect_RecipeURL,{
browser()
if(Log_Flag) Log_App_Activity('shiny::selectInput -> InSelect_RecipeURL')
})
}
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# RUN APPLICATION
#-------------------------------------------------------------------------------------------------
shiny::shinyApp(ui = Main_UI, server = server)
#-------------------------------------------------------------------------------------------------
这是 sessionInfo()
的输出:
R version 3.6.2 (2019-12-12)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)
Matrix products: default
locale:
[1] LC_COLLATE=Polish_Poland.1250 LC_CTYPE=Polish_Poland.1250 LC_MONETARY=Polish_Poland.1250 LC_NUMERIC=C
[5] LC_TIME=Polish_Poland.1250
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] mgsub_1.7.3 stringr_1.4.0 dplyr_1.0.2 DT_0.14 reactlog_1.1.0 shinydashboard_0.7.1
[7] shiny_1.4.0
loaded via a namespace (and not attached):
[1] Rcpp_1.0.5 rstudioapi_0.13 magrittr_1.5 tidyselect_1.1.0 xtable_1.8-4 R6_2.4.1 rlang_0.4.11 fastmap_1.0.1
[9] tools_3.6.2 ellipsis_0.3.2 crosstalk_1.1.0.1 htmltools_0.4.0 yaml_2.2.1 digest_0.6.25 tibble_2.1.3 lifecycle_1.0.0
[17] crayon_1.3.4 purrr_0.3.3 later_1.0.0 htmlwidgets_1.5.1 vctrs_0.3.4 promises_1.1.0 glue_1.4.1 mime_0.9
[25] stringi_1.4.6 compiler_3.6.2 pillar_1.4.3 generics_0.0.2 jsonlite_1.7.0 httpuv_1.5.2 pkgconfig_2.0.3
Recipe_Inv
变量具有以下(20 个示例行)结构:
Recipe_Nm
Recipe_URL
Tags
Apple Butter
https://paleoleap.com/apple-butter/
PALEO DESSERTS;SWEETS AND SNACKS;COOKING: FAST PREP;COOKING: SLOW-COOKER;PALEO AUTOIMMUNE-FRIENDLY RECIPES;DIET: EGG-FREE;DIET: NUT-FREE;DIET: VEGETARIAN;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
Baked Eggs With Asparagus and Leeks
https://paleoleap.com/baked-eggs-asparagus-leeks/
PALEO PORK RECIPES;PALEO EGG RECIPES;PALEO LOW-CARB RECIPES;PALEO BREAKFAST RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: NUT-FREE;PALEO BUDGET-FRIENDLY RECIPES
Beef Pho
https://paleoleap.com/beef-pho/
PALEO BEEF AND RED MEAT RECIPES;PALEO SOUP RECIPES;PALEO BREAKFAST RECIPES;PALEO LUNCH RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;PALEO AUTOIMMUNE-FRIENDLY RECIPES;DIET: EGG-FREE;DIET: LOW-FODMAP;DIET: NUT-FREE
Chicken Cashew Casserole
https://paleoleap.com/chicken-cashew-casserole/
PALEO CHICKEN AND POULTRY RECIPES;PALEO LOW-CARB RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;DIET: EGG-FREE
Chicken With Garlic-Roasted Sweet Potatoes
https://paleoleap.com/chicken-garlic-roasted-sweet-potatoes/
PALEO CHICKEN AND POULTRY RECIPES;PALEO DINNER RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
Crab-Stuffed Deviled Eggs With Tarragon
https://paleoleap.com/crab-stuffed-deviled-eggs-tarragon/
PALEO FISH AND SEAFOOD RECIPES;PALEO EGG RECIPES;PALEO LOW-CARB RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: LOW-FODMAP;DIET: NUT-FREE;GOOD FOR LEFTOVERS
Elk Shepherd’s Pie
https://paleoleap.com/elk-shepherd-pie/
PALEO BEEF AND RED MEAT RECIPES;PALEO DINNER RECIPES;DIET: EGG-FREE;DIET: NUT-FREE;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
Fresh Fruit And Kale Salad
https://paleoleap.com/fresh-fruit-and-kale-salad/
PALEO SALAD RECIPES;PALEO BREAKFAST RECIPES;COOKING: FAST PREP;DIET: EGG-FREE;DIET: VEGETARIAN
Garlic-Roasted Cherry Tomatoes
https://paleoleap.com/garlic-roasted-cherry-tomatoes/
PALEO SIDES;VEGGIES AND APPETIZERS;PALEO LOW-CARB RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE;DIET: VEGETARIAN;PALEO BUDGET-FRIENDLY RECIPES
Ginger Carrot Soup
https://paleoleap.com/ginger-carrot-soup/
PALEO SOUP RECIPES;PALEO LOW-CARB RECIPES;PALEO BREAKFAST RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;PALEO AUTOIMMUNE-FRIENDLY RECIPES;DIET: EGG-FREE;DIET: NUT-FREE;DIET: VEGETARIAN;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS
Grilled Pineapple Chicken
https://paleoleap.com/grilled-pineapple-chicken/
PALEO CHICKEN AND POULTRY RECIPES;PALEO DINNER RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;COOKING: GRILL;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
Italian-Style Fish Bowls
https://paleoleap.com/italian-style-fish-bowl/
PALEO FISH AND SEAFOOD RECIPES;PALEO LOW-CARB RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE
Keto Slow Cooker Chicken Soup
https://paleoleap.com/slow-cooker-chicken-soup/
PALEO CHICKEN AND POULTRY RECIPES;PALEO SOUP RECIPES;PALEO LOW-CARB RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;COOKING: SLOW-COOKER;DIET: EGG-FREE;DIET: NUT-FREE;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
Maple-Barbecue Ribs
https://paleoleap.com/maple-barbecue-ribs/
PALEO PORK RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
Paleo Garlic Shrimp With Zucchini Noodles
https://paleoleap.com/garlic-shrimp-with-zucchini-noodle/
PALEO FISH AND SEAFOOD RECIPES;PALEO LOW-CARB RECIPES;PALEO LUNCH RECIPES;PALEO DINNER RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;PALEO AUTOIMMUNE-FRIENDLY RECIPES;DIET: EGG-FREE;PALEO KID-FRIENDLY RECIPES
Pumpkin Cookies
https://paleoleap.com/pumpkin-cookies/
PALEO DESSERTS;SWEETS AND SNACKS;COOKING: FAST COOK;COOKING: FAST PREP;DIET: EGG-FREE;DIET: VEGETARIAN;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
Sautéed Chicken And Cabbage
https://paleoleap.com/sauteed-chicken-cabbage/
PALEO CHICKEN AND POULTRY RECIPES;PALEO LOW-CARB RECIPES;PALEO LUNCH RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
Slow Cooker Butterkin And Nuts
https://paleoleap.com/slow-cooker-butterkin-and-nuts/
PALEO SIDES;VEGGIES AND APPETIZERS;COOKING: FAST PREP;COOKING: SLOW-COOKER;DIET: EGG-FREE;DIET: VEGETARIAN;PALEO BUDGET-FRIENDLY RECIPES;PALEO KID-FRIENDLY RECIPES
Slow Cooker Curry Chicken
https://paleoleap.com/slow-cooker-curry-chicken/
PALEO CHICKEN AND POULTRY RECIPES;PALEO DINNER RECIPES;COOKING: SLOW-COOKER;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS
Special Sweet Potato Salad
https://paleoleap.com/special-sweet-potato-salad/
PALEO SALAD RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: NUT-FREE;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES
当我在 RStudio 中启动我的应用程序时,我的启动屏幕如下所示:
在我点击补充工具栏上的 menuItem -> tbRecipeLs
后,触发 DT::renderDT -> OutDT_RecipeList
函数调用,进而触发 reactive -> Recipe_Inv_Flt()
变量,如 Log_App_Activity
UDF:
[1] "DT::renderDT -> OutDT_RecipeList"
[1] "shiny::reactive -> Recipe_Inv_Flt"
这会在仪表板主体中显示 UI 输出 DT::DTOutput -> OutDT_RecipeList
:
我尝试通过在下拉列表中选择标签过滤器来过滤掉列表中的食谱 selectInput -> InSelect_RecipeTags
,见下图:
并根据 Log_App_Activity
UDF 控制台输出触发以下 UI 和服务器组件:
[1] "shiny::selectInput -> InSelect_RecipeTags"
[1] "DT::renderDT -> OutDT_RecipeList"
我的期望是,因为 DT::renderDT -> OutDT_RecipeList
被调用,所以 reactive -> Recipe_Inv_Flt()
变量也应该被再次调用,因为它在 DT::renderDT -> OutDT_RecipeList
函数内部,也因为它的依赖性,下拉字段 selectInput -> InSelect_RecipeTags
已更改。然而,情况似乎并非如此,因为数据 table UI 输出 DT::DTOutput -> OutDT_RecipeList
仍然显示所有行 (1,555),但是它确实从Tags 列,基于在下拉字段 selectInput -> InSelect_RecipeTags
中选择的过滤器值,由 ReArrange_Tags
DT::renderDT -> OutDT_RecipeList
函数内部的 UDF 调用。我应该怎么做才能使 reactive -> Recipe_Inv_Flt()
变量在每次从 DT::renderDT -> OutDT_RecipeList
函数内部调用时重新计算?
根据@MrFlick 的评论,错字是问题所在:
在reactive -> Recipe_Inv_Flt()
定义如下if
语句条件:
if(is.null(input$InSelect_Recipe_Tags))
应该是(没有最后一个下划线):
if(is.null(input$InSelect_RecipeTags))
问题已解决,非常感谢!
我闪亮的应用程序中的反应值在第一次计算后从 DT::renderDT 函数内部调用时不会重新计算。
这是我的代码:
#-------------------------------------------------------------------------------------------------
# ENVIRONMENT & PACKAGES
#-------------------------------------------------------------------------------------------------
# Set working directory
setwd('C:/Users/username/OneDrive/Desktop/Coding projects/Paleo Diet Planner')
# Clear workspace
rm(list = ls())
# Package/library list
pckgs <- c('shiny','shinydashboard','reactlog','DT','dplyr','stringr','mgsub')
# Install and load libraries
for(pckg in pckgs)
{
if(!(pckg %in% rownames(installed.packages()))) install.packages(pckg)
if(!(pckg %in% (.packages()))) library(pckg, character.only = TRUE)
}
# Remove unnecessary variables
rm(pckg,pckgs)
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# DATA
#-------------------------------------------------------------------------------------------------
# Static Data
Recipe_Inv <- readRDS('./Data/Recipe_Inv.rds')
# Get unique tags
Recipe_Tags <- Recipe_Inv$Tags %>% stringr::str_split(., ';', simplify = T) %>%
trimws %>% as.vector %>% unique %>% magrittr::extract(. != '') %>% magrittr::extract(order(.))
# Utility variables
Debug_Flag <- F
Log_Flag <- T
LogFile <- c()
LogFile_Path <- as.character(Sys.time()) %>% mgsub(., c('-', ' ', ':'), c('', '_', '-')) %>% paste0('./Logs/',.,'.txt')
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# UTILITY FUNCTIONS
#-------------------------------------------------------------------------------------------------
# Function for checking if the tags chosen for filtering are present in the tag string, i.e.
# string with tags separated by semi-colon
Filter_Tags <- function(Tags, Tag_Crit){
# Debug and log
# if(Log_Flag) print('Utility_Function -> Filter_Tags')
if(Debug_Flag) browser()
#Extract tags from the tag string as character vector
Tag_Ls <- trimws(stringr::str_split(Tags, ';')[[1]])
# Check if the intersection (common elements) of the tag string and filter tags vector have the same length
# Equivalent to all filter tags being present in the tag string
length(intersect(Tag_Ls, Tag_Crit)) == length(Tag_Crit)
}
# Function to filter out, rearrange and order the tags when select drop-down field is used
ReArrange_Tags <- function(Tags, Tag_Crit){
# Log
# if(Log_Flag) print('Utility_Function - > ReArrange_Tags')
if(is.null(Tag_Crit)){
# If the select drop-down list is empty, leave tag string (tags delimited by semi-colon) as-is
Tags
}else{
# Debug
if(Debug_Flag) browser()
# Get the tags in the tag string that were chosen in the select drop-down field
# and order them aplhabetically
trimws(stringr::str_split(Tags, ';')[[1]]) %>%
intersect(., Tag_Crit) %>% magrittr::extract(order(.)) %>%
paste0(., collapse = ';')
}
}
# Function for logging events in the apps in terms of UI element usage and server activity
Log_App_Activity <- function(print_txt){
print(print_txt)
LogFile <<- c(LogFile,print_txt)
write.table(LogFile, LogFile_Path, sep = '\n', col.names = F, row.names = F)
}
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# USER INTERFACE
#-------------------------------------------------------------------------------------------------
#### Separate components ####
# Dashboard Header
db_header <- shinydashboard::dashboardHeader(
# Dashboard title
title = 'Paleo Diet Planner'
)
# Dashboard Sidebar
db_Sidebar <- shinydashboard::dashboardSidebar(
#### Sidebar settings ####
# ID of the dashboard sidebar object
id = 'InSidebar_Menu',
# Width setting
width = 350,
#### UI elements ####
#
shiny::selectInput(inputId = 'InSelect_RecipeTags',
label = 'Select Recipe Categories',
choices = Recipe_Tags, multiple = T),
#
shinydashboard::menuItem(text = 'Recipe List', tabName = 'tbRecipeLs',
icon = shiny::icon('th-list')),
#
shinydashboard::menuItem(text = 'Recipe View', tabName = 'tbRecipeView',
icon = shiny::icon('readme'))
)
# Dashboard Body
db_Body <- shinydashboard::dashboardBody(
#
shinydashboard::tabItems(
#
shinydashboard::tabItem(
#
tabName = 'tbRecipeLs',
#
DT::DTOutput(outputId = 'OutDT_RecipeList')
),
#
shinydashboard::tabItem(
#
tabName = 'tbRecipeView',
#
shiny::uiOutput('OutUI_RecipeURL'),
#
shiny::htmlOutput("OutUI_RecipeWebsite")
)
)
)
#### Main UI ####
Main_UI <- dashboardPage(db_header, db_Sidebar, db_Body)
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# SERVER
#-------------------------------------------------------------------------------------------------
# Define server logic
server <- function(input, output){
# Filtered Recipe Inventory based on the select drop-down field 'InSelect_RecipeTags'
Recipe_Inv_Flt <- shiny::reactive({
# Debug and log
if(Log_Flag) Log_App_Activity('shiny::reactive -> Recipe_Inv_Flt')
if(Debug_Flag) browser()
# Check if the select drop-down field 'InSelect_RecipeTags' has been used and filter appropriately
if(is.null(input$InSelect_Recipe_Tags)){
Recipe_Inv
}else{
Recipe_Inv %>% dplyr::rowwise() %>%
dplyr::filter(Filter_Tags(Tags, input$InSelect_RecipeTags)) %>%
dplyr::ungroup()
}
})
# Data Table displaying the reactive values Recipe_Inv_Flt() with appropriate tags displayed
# in the tag column, based on the tags selected in the select drop-down field 'InSelect_RecipeTags'
output$OutDT_RecipeList <- DT::renderDT({
# Debug and log
if(Log_Flag) Log_App_Activity('DT::renderDT -> OutDT_RecipeList')
if(Debug_Flag) browser()
#
Recipe_Inv_Flt() %>% dplyr::select(Tags, Recipe_Nm) %>% dplyr::rowwise() %>%
dplyr::mutate(Tags = ReArrange_Tags(Tags, input$InSelect_RecipeTags)) %>%
dplyr::ungroup() %>% dplyr::arrange(Tags)
})
#
output$OutUI_RecipeURL <- shiny::renderUI({
# Debug and log
if(Log_Flag) Log_App_Activity('shiny::renderUI -> OutUI_RecipeURL')
if(Debug_Flag) browser(text = 'shiny::renderUI -> OutUI_RecipeURL')
#
choices <- Recipe_Inv_Flt() %>% .$Recipe_Nm %>% magrittr::extract(input$OutDT_RecipeList_rows_selected)
#
shiny::selectInput(inputId = 'InSelect_RecipeURL',
label = 'Select recipe to display',
choices = choices, multiple = F)
})
#
output$OutUI_RecipeWebsite <- shiny::renderUI({
#Debug and log
if(Log_Flag) Log_App_Activity('shiny::renderUI -> OutUI_RecipeWebsite')
if(Debug_Flag) browser(text = 'shiny::renderUI -> OutUI_RecipeWebsite')
#
if(!is.null(input$InSelect_RecipeURL)){
Recipe_URL <- Recipe_Inv %>% dplyr::filter(Recipe_Nm == input$InSelect_RecipeURL) %>% .$Recipe_URL
}else{
Recipe_URL <- NA
}
#
shiny::tags$iframe(src = Recipe_URL, width = "100%", height = 800)
})
# Log for UI elements
shiny::observeEvent(input$InSelect_RecipeTags,{
browser()
if(Log_Flag) Log_App_Activity('shiny::selectInput -> InSelect_RecipeTags')
})
shiny::observeEvent(input$InSidebar_Menu,{
browser()
if(Log_Flag){
switch(input$InSidebar_Menu,
'tbRecipeLs' = Log_App_Activity('shinydashboard::menuIte -> tbRecipeLs'),
'tbRecipeView' = Log_App_Activity('shinydashboard::menuIte -> tbRecipeView'))
}
})
shiny::observeEvent(input$InSelect_RecipeURL,{
browser()
if(Log_Flag) Log_App_Activity('shiny::selectInput -> InSelect_RecipeURL')
})
}
#-------------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------------
# RUN APPLICATION
#-------------------------------------------------------------------------------------------------
shiny::shinyApp(ui = Main_UI, server = server)
#-------------------------------------------------------------------------------------------------
这是 sessionInfo()
的输出:
R version 3.6.2 (2019-12-12)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)
Matrix products: default
locale:
[1] LC_COLLATE=Polish_Poland.1250 LC_CTYPE=Polish_Poland.1250 LC_MONETARY=Polish_Poland.1250 LC_NUMERIC=C
[5] LC_TIME=Polish_Poland.1250
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] mgsub_1.7.3 stringr_1.4.0 dplyr_1.0.2 DT_0.14 reactlog_1.1.0 shinydashboard_0.7.1
[7] shiny_1.4.0
loaded via a namespace (and not attached):
[1] Rcpp_1.0.5 rstudioapi_0.13 magrittr_1.5 tidyselect_1.1.0 xtable_1.8-4 R6_2.4.1 rlang_0.4.11 fastmap_1.0.1
[9] tools_3.6.2 ellipsis_0.3.2 crosstalk_1.1.0.1 htmltools_0.4.0 yaml_2.2.1 digest_0.6.25 tibble_2.1.3 lifecycle_1.0.0
[17] crayon_1.3.4 purrr_0.3.3 later_1.0.0 htmlwidgets_1.5.1 vctrs_0.3.4 promises_1.1.0 glue_1.4.1 mime_0.9
[25] stringi_1.4.6 compiler_3.6.2 pillar_1.4.3 generics_0.0.2 jsonlite_1.7.0 httpuv_1.5.2 pkgconfig_2.0.3
Recipe_Inv
变量具有以下(20 个示例行)结构:
Recipe_Nm | Recipe_URL | Tags |
---|---|---|
Apple Butter | https://paleoleap.com/apple-butter/ | PALEO DESSERTS;SWEETS AND SNACKS;COOKING: FAST PREP;COOKING: SLOW-COOKER;PALEO AUTOIMMUNE-FRIENDLY RECIPES;DIET: EGG-FREE;DIET: NUT-FREE;DIET: VEGETARIAN;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
Baked Eggs With Asparagus and Leeks | https://paleoleap.com/baked-eggs-asparagus-leeks/ | PALEO PORK RECIPES;PALEO EGG RECIPES;PALEO LOW-CARB RECIPES;PALEO BREAKFAST RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: NUT-FREE;PALEO BUDGET-FRIENDLY RECIPES |
Beef Pho | https://paleoleap.com/beef-pho/ | PALEO BEEF AND RED MEAT RECIPES;PALEO SOUP RECIPES;PALEO BREAKFAST RECIPES;PALEO LUNCH RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;PALEO AUTOIMMUNE-FRIENDLY RECIPES;DIET: EGG-FREE;DIET: LOW-FODMAP;DIET: NUT-FREE |
Chicken Cashew Casserole | https://paleoleap.com/chicken-cashew-casserole/ | PALEO CHICKEN AND POULTRY RECIPES;PALEO LOW-CARB RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;DIET: EGG-FREE |
Chicken With Garlic-Roasted Sweet Potatoes | https://paleoleap.com/chicken-garlic-roasted-sweet-potatoes/ | PALEO CHICKEN AND POULTRY RECIPES;PALEO DINNER RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
Crab-Stuffed Deviled Eggs With Tarragon | https://paleoleap.com/crab-stuffed-deviled-eggs-tarragon/ | PALEO FISH AND SEAFOOD RECIPES;PALEO EGG RECIPES;PALEO LOW-CARB RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: LOW-FODMAP;DIET: NUT-FREE;GOOD FOR LEFTOVERS |
Elk Shepherd’s Pie | https://paleoleap.com/elk-shepherd-pie/ | PALEO BEEF AND RED MEAT RECIPES;PALEO DINNER RECIPES;DIET: EGG-FREE;DIET: NUT-FREE;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
Fresh Fruit And Kale Salad | https://paleoleap.com/fresh-fruit-and-kale-salad/ | PALEO SALAD RECIPES;PALEO BREAKFAST RECIPES;COOKING: FAST PREP;DIET: EGG-FREE;DIET: VEGETARIAN |
Garlic-Roasted Cherry Tomatoes | https://paleoleap.com/garlic-roasted-cherry-tomatoes/ | PALEO SIDES;VEGGIES AND APPETIZERS;PALEO LOW-CARB RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE;DIET: VEGETARIAN;PALEO BUDGET-FRIENDLY RECIPES |
Ginger Carrot Soup | https://paleoleap.com/ginger-carrot-soup/ | PALEO SOUP RECIPES;PALEO LOW-CARB RECIPES;PALEO BREAKFAST RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;PALEO AUTOIMMUNE-FRIENDLY RECIPES;DIET: EGG-FREE;DIET: NUT-FREE;DIET: VEGETARIAN;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS |
Grilled Pineapple Chicken | https://paleoleap.com/grilled-pineapple-chicken/ | PALEO CHICKEN AND POULTRY RECIPES;PALEO DINNER RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;COOKING: GRILL;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
Italian-Style Fish Bowls | https://paleoleap.com/italian-style-fish-bowl/ | PALEO FISH AND SEAFOOD RECIPES;PALEO LOW-CARB RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE |
Keto Slow Cooker Chicken Soup | https://paleoleap.com/slow-cooker-chicken-soup/ | PALEO CHICKEN AND POULTRY RECIPES;PALEO SOUP RECIPES;PALEO LOW-CARB RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;COOKING: SLOW-COOKER;DIET: EGG-FREE;DIET: NUT-FREE;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
Maple-Barbecue Ribs | https://paleoleap.com/maple-barbecue-ribs/ | PALEO PORK RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
Paleo Garlic Shrimp With Zucchini Noodles | https://paleoleap.com/garlic-shrimp-with-zucchini-noodle/ | PALEO FISH AND SEAFOOD RECIPES;PALEO LOW-CARB RECIPES;PALEO LUNCH RECIPES;PALEO DINNER RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;PALEO AUTOIMMUNE-FRIENDLY RECIPES;DIET: EGG-FREE;PALEO KID-FRIENDLY RECIPES |
Pumpkin Cookies | https://paleoleap.com/pumpkin-cookies/ | PALEO DESSERTS;SWEETS AND SNACKS;COOKING: FAST COOK;COOKING: FAST PREP;DIET: EGG-FREE;DIET: VEGETARIAN;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
Sautéed Chicken And Cabbage | https://paleoleap.com/sauteed-chicken-cabbage/ | PALEO CHICKEN AND POULTRY RECIPES;PALEO LOW-CARB RECIPES;PALEO LUNCH RECIPES;PALEO DINNER RECIPES;COOKING: FAST PREP;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
Slow Cooker Butterkin And Nuts | https://paleoleap.com/slow-cooker-butterkin-and-nuts/ | PALEO SIDES;VEGGIES AND APPETIZERS;COOKING: FAST PREP;COOKING: SLOW-COOKER;DIET: EGG-FREE;DIET: VEGETARIAN;PALEO BUDGET-FRIENDLY RECIPES;PALEO KID-FRIENDLY RECIPES |
Slow Cooker Curry Chicken | https://paleoleap.com/slow-cooker-curry-chicken/ | PALEO CHICKEN AND POULTRY RECIPES;PALEO DINNER RECIPES;COOKING: SLOW-COOKER;DIET: EGG-FREE;DIET: NUT-FREE;GOOD FOR LEFTOVERS |
Special Sweet Potato Salad | https://paleoleap.com/special-sweet-potato-salad/ | PALEO SALAD RECIPES;COOKING: FAST COOK;COOKING: FAST PREP;DIET: NUT-FREE;PALEO BUDGET-FRIENDLY RECIPES;GOOD FOR LEFTOVERS;PALEO KID-FRIENDLY RECIPES |
当我在 RStudio 中启动我的应用程序时,我的启动屏幕如下所示:
在我点击补充工具栏上的 menuItem -> tbRecipeLs
后,触发 DT::renderDT -> OutDT_RecipeList
函数调用,进而触发 reactive -> Recipe_Inv_Flt()
变量,如 Log_App_Activity
UDF:
[1] "DT::renderDT -> OutDT_RecipeList"
[1] "shiny::reactive -> Recipe_Inv_Flt"
这会在仪表板主体中显示 UI 输出 DT::DTOutput -> OutDT_RecipeList
:
我尝试通过在下拉列表中选择标签过滤器来过滤掉列表中的食谱 selectInput -> InSelect_RecipeTags
,见下图:
并根据 Log_App_Activity
UDF 控制台输出触发以下 UI 和服务器组件:
[1] "shiny::selectInput -> InSelect_RecipeTags"
[1] "DT::renderDT -> OutDT_RecipeList"
我的期望是,因为 DT::renderDT -> OutDT_RecipeList
被调用,所以 reactive -> Recipe_Inv_Flt()
变量也应该被再次调用,因为它在 DT::renderDT -> OutDT_RecipeList
函数内部,也因为它的依赖性,下拉字段 selectInput -> InSelect_RecipeTags
已更改。然而,情况似乎并非如此,因为数据 table UI 输出 DT::DTOutput -> OutDT_RecipeList
仍然显示所有行 (1,555),但是它确实从Tags 列,基于在下拉字段 selectInput -> InSelect_RecipeTags
中选择的过滤器值,由 ReArrange_Tags
DT::renderDT -> OutDT_RecipeList
函数内部的 UDF 调用。我应该怎么做才能使 reactive -> Recipe_Inv_Flt()
变量在每次从 DT::renderDT -> OutDT_RecipeList
函数内部调用时重新计算?
根据@MrFlick 的评论,错字是问题所在:
在reactive -> Recipe_Inv_Flt()
定义如下if
语句条件:
if(is.null(input$InSelect_Recipe_Tags))
应该是(没有最后一个下划线):
if(is.null(input$InSelect_RecipeTags))
问题已解决,非常感谢!