r shiny – communication of inputId between shiny modules in order to use shinyjs functions like disable/enable/toggle

huangapple go评论59阅读模式
英文:

r shiny - communication of inputId between shiny modules in order to use shinyjs functions like disable/enable/toggle

问题

I've been delving into Shiny modules, and so far, everything has been going well. However, I'm having trouble manipulating inputIds that come from another module. Specifically, I want to use functions like shinyjs::disable to disable a button in one Shiny module by pressing a button in another module (same applies for numericInput, selectizeInput, etc).

I've considered using R6, but it may add unnecessary complexity to the app, given its simplicity. Nonetheless, I'm open to suggestions that involve or do not involve an R6/gargoyle approach.

Here is a toy example that summarizes the issue and what I've tried so far.

(以下为代码部分)

Any comments or suggestions are welcome.

英文:

I've been delving into Shiny modules, and so far, everything has been going well. However, I'm having trouble manipulating inputIds that come from another module. Specifically, I want to use functions like shinyjs::disable to disable a button in one Shiny module by pressing a button in another module (same applies for numericInput, selectizeInput, etc).

I've considered using R6, but it may add unnecessary complexity to the app, given its simplicity. Nonetheless, I'm open to suggestions that involve or do not involve an R6/gargoyle approach.

Here is a toy example that summarises the issue and what I've tried so far.



selectUI <- function(id) {
  ns <- NS(id)
  tagList(
    fluidRow(
      column(width = 4, offset = 0, align = "center",
      selectizeInput(inputId = ns("item"), 
                     label = "selection",
                     choices = c("", "a", "b", "c"), 
                     selected = "")
      ),
      column(width = 8, offset = 0)
    )
  )
}

selectServer <- function(id) {
  moduleServer(
    id,
    function(input, output, session) {
      return (
        list(
          selection = shiny::reactive(input$item)
        )
      )
    }
  )
}

buttonUI <- function(id) {
  ns <- NS(id)
  tagList(
    fluidRow(
      column(width = 2, offset = 0, align = "center",
             circleButton(inputId = ns("btn"))
      ),
      column(width = 10, offset = 0)
    ))
}

buttonServer <- function(id, item) {
  moduleServer(
    id,
    function(input, output, session) {
      observeEvent(
        eventExpr = input$btn, 
        handlerExpr = {
          #' does not work, i've tried with item(), item()$inputId, item$inputId() 
          #' and also hardcoding the id with and without the ns prefix ("item", "select-item", "button-select-item")
          shinyjs::disable("item")  # the issue ----
        },
        ignoreNULL = TRUE,
        ignoreInit = TRUE
      )
      
      #'debug
      observe({
        #' input from module selectUI & selectServer identified correctly
        showNotification(item(), duration = 5) 
      })
    }
  )
}

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),
  selectUI(id = "select"),
  buttonUI(id = "button"),
)

server <- function(input, output, session) {
  
  item <- selectServer(id = "select")
  buttonServer(id = "button", item = item$selection)

}

shinyApp(ui, server)

Any comments or suggestions are welcome.

答案1

得分: 1

以下是翻译好的内容:

这里有一个解决方案,它确实使用了 gargoyle,因为这是一个通常有效的简单解决方案。gargoyle 包只是对在 session$userData 对象内部使用 reactiveVal 的一个良好封装。

另一种选择是将一个模块的反应传递到另一个模块。这也可以正常工作,但当您希望管理多个“更新信号”时,就像您在这里所做的那样(即在模块中的某处发生某事时禁用/启用UI的部分),就会成为一个问题。

selectUI <- function(id) {
  ns <- NS(id)
  tagList(
    fluidRow(
      column(width = 4, offset = 0, align = "center",
             selectizeInput(inputId = ns("item"), 
                            label = "selection",
                            choices = c("", "a", "b", "c"), 
                            selected = "")
      ),
      column(width = 8, offset = 0)
    )
  )
}

selectServer <- function(id) {
  moduleServer(
    id,
    function(input, output, session) {
      
      observeEvent(gargoyle::watch("disable_button"), ignoreInit = TRUE, {
        shinyjs::disable("item")
      })
      
      return (
        list(
          selection = shiny::reactive(input$item)
        )
      )
    }
  )
}

buttonUI <- function(id) {
  ns <- NS(id)
  tagList(
    fluidRow(
      column(width = 2, offset = 0, align = "center",
             circleButton(inputId = ns("btn"))
      ),
      column(width = 10, offset = 0)
    ))
}

buttonServer <- function(id, item) {
  moduleServer(
    id,
    function(input, output, session) {
      observeEvent(input$btn, {
        gargoyle::trigger("disable_button")
      })
      
      # 调试
      observe({
        # 来自模块 selectUI 和 selectServer 的输入正确识别
        showNotification(item(), duration = 5) 
      })
      
    }
  )
}

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),
  
  selectUI(id = "select"),
  buttonUI(id = "button"),
)

server <- function(input, output, session) {
  
  gargoyle::init("disable_button")
  item <- selectServer(id = "select")
  buttonServer(id = "button", item = item$selection)
  
}

shinyApp(ui, server)
英文:

Here is a solution that does use gargoyle, because it is such a simple solution that will work generally. The gargoyle package is just a nice wrapper around the use of reactiveVal's inside the session$userData object.

The alternative would be to pass reactives from one module to another. That will work fine too but becomes a problem when you want to manage multiple 'update signals' like you are doing here (i.e. disable/enable parts of the UI when something happens in a module, somewhere).

selectUI &lt;- function(id) {
ns &lt;- NS(id)
tagList(
fluidRow(
column(width = 4, offset = 0, align = &quot;center&quot;,
selectizeInput(inputId = ns(&quot;item&quot;), 
label = &quot;selection&quot;,
choices = c(&quot;&quot;, &quot;a&quot;, &quot;b&quot;, &quot;c&quot;), 
selected = &quot;&quot;)
),
column(width = 8, offset = 0)
)
)
}
selectServer &lt;- function(id) {
moduleServer(
id,
function(input, output, session) {
observeEvent(gargoyle::watch(&quot;disable_button&quot;), ignoreInit = TRUE, {
shinyjs::disable(&quot;item&quot;)
})
return (
list(
selection = shiny::reactive(input$item)
)
)
}
)
}
buttonUI &lt;- function(id) {
ns &lt;- NS(id)
tagList(
fluidRow(
column(width = 2, offset = 0, align = &quot;center&quot;,
circleButton(inputId = ns(&quot;btn&quot;))
),
column(width = 10, offset = 0)
))
}
buttonServer &lt;- function(id, item) {
moduleServer(
id,
function(input, output, session) {
observeEvent(input$btn, {
gargoyle::trigger(&quot;disable_button&quot;)
})
#&#39;debug
observe({
#&#39; input from module selectUI &amp; selectServer identified correctly
showNotification(item(), duration = 5) 
})
}
)
}
library(shiny)
library(shinyjs)
ui &lt;- fluidPage(
useShinyjs(),
selectUI(id = &quot;select&quot;),
buttonUI(id = &quot;button&quot;),
)
server &lt;- function(input, output, session) {
gargoyle::init(&quot;disable_button&quot;)
item &lt;- selectServer(id = &quot;select&quot;)
buttonServer(id = &quot;button&quot;, item = item$selection)
}
shinyApp(ui, server)

huangapple
  • 本文由 发表于 2023年3月4日 01:52:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75630361.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定