从 DT::renderDT 调用时,R 闪亮的反应值不会重新计算
Posted
技术标签:
【中文标题】从 DT::renderDT 调用时,R 闪亮的反应值不会重新计算【英文标题】:R shiny reactive value does not recalculate when called from DT::renderDT 【发布时间】:2021-10-21 08:46:27 【问题描述】:在第一次计算后从 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
被改变。但是,情况似乎并非如此,因为数据表 UI 输出 DT::DTOutput -> OutDT_RecipeList
仍然显示所有行 (1,555),但它确实从 标签中删除了标签> 列,基于下拉字段selectInput -> InSelect_RecipeTags
中选择的过滤器值,这是由DT::renderDT -> OutDT_RecipeList
函数内的ReArrange_Tags
UDF 调用完成的。每次从 DT::renderDT -> OutDT_RecipeList
函数内部调用 reactive -> Recipe_Inv_Flt()
变量时,我应该怎么做才能重新计算它?
【问题讨论】:
你有一个错字。你有if(is.null(input$InSelect_Recipe_Tags))
,但它应该是if(is.null(input$InSelect_RecipeTags))
(没有额外的下划线)。
@MrFlick,就是这样,非常感谢!
【参考方案1】:
根据@MrFlick 的评论,错字是问题所在:
在reactive -> Recipe_Inv_Flt()
的定义中if
声明条件如下:
if(is.null(input$InSelect_Recipe_Tags))
应该是(没有最后一个下划线):
if(is.null(input$InSelect_RecipeTags))
问题已解决,非常感谢!
【讨论】:
我还注意到shinydashboard::menuItem
UI 元素没有包裹在 shinydashboard::sidebarMenu
中,这导致了应用程序的一些不良行为,以及 id
属性显示在侧边栏(这是因为shinydashboard::dashboardSidebar
UI 元素没有id
属性,该属性属于shinydashboard::sidebarMenu
)。以上是关于从 DT::renderDT 调用时,R 闪亮的反应值不会重新计算的主要内容,如果未能解决你的问题,请参考以下文章