• R语言
  • [探讨]:Shiny app 里不用 `observeEvent()` ,`updateXXnput()` 和`shinyjs` 设置复位按钮的方法

最近发现一个简洁的方法设置复位按钮,不需要用到额外的包或observeEvent() ,updateXXnput()。有哪位 html 大神能解释一下是什么原理吗?我现在知其然不知其所以然。

library(shiny)
runApp(list(
  ui = pageWithSidebar(
    headerPanel("Example"),
    sidebarPanel(
      uiOutput('resetable_input'),
      actionButton("reset_input", "Reset inputs")
     ),
    mainPanel(
      verbatimTextOutput("summary")
    )
  ),
  
  server = function(input, output, session) {
    
    output$summary <- renderText({
      return(paste(input$integer, input$mynumber))
    })
    
    output$resetable_input <- renderUI({
      input$reset_input
      div(id="ID_A",
          numericInput("mynumber", "Enter a number", 20),
          sliderInput("integer", "Integer:",
                      min = 0, max = 1000,
                      value = 500))
    })
  }
))

这里的 input$reset_input 并没有连接到任何的逻辑,直接放到 renderUI() 里就行了。我一开始觉得会不会是一定要 0,1,2,...(actionButton 的输出) 的数字,所以把input$reset_input 换成 string,如下,同样行得通 (把 text 改动前面两个 input 回到原始数值)。

library(shiny)
runApp(list(
  ui = pageWithSidebar(
    headerPanel("Example"),
    sidebarPanel(
      uiOutput('resetable_input'),
      textInput("reset_input", "Enter a text", "test")
    ),
    mainPanel(
      verbatimTextOutput("summary")
    )
  ),
  
  server = function(input, output, session) {
    
    output$summary <- renderText({
      return(paste(input$integer, input$mynumber))
    })
    
    output$resetable_input <- renderUI({
      input$reset_input
      div(id="ID_A",
          numericInput("mynumber", "Enter a number", 20),
          sliderInput("integer", "Integer:",
                      min = 0, max = 1000,
                      value = 500))
    })
  }
))

问题:

  1. 为什么在 renderUI() 里,div 前加一个变量,这个变量会重设 div() 里面的 input 为初始值?
  2. 如果这个方法行得通,可否代替 updateXXInput() 的功能?

谢谢!

不了解shiny不过你这两个问题都不是问题呀…

这个 …… 本身自带逻辑。第一次加载的时候,output$resetable_input 直接渲染了 div 里边的内容。之后只要每次点击 input$reset_input,这输入值就变了,按照 reactive 的依赖关系,output$resetable_input 就会被重新渲染,虽然最后还是渲染的原始的 div。

这个依赖关系可以用 reactlog 看得很清楚 (打开交互完按 Ctrl/Command + F3):

library("shiny")
library("reactlog")

options(shiny.reactlog = TRUE)

runApp(list(
  ui = pageWithSidebar(
    headerPanel("Example"),
    sidebarPanel(
      uiOutput("resetable_input"),
      actionButton("reset_input", "Reset inputs")
    ),
    mainPanel(
      verbatimTextOutput("summary")
    )
  ),

  server = function(input, output, session) {
    output$summary <- renderText({
      return(paste(input$integer, input$mynumber))
    })

    output$resetable_input <- renderUI({
      input$reset_input
      div(
        id = "ID_A",
        numericInput("mynumber", "Enter a number", 20),
        sliderInput("integer", "Integer:",
          min = 0, max = 1000,
          value = 500
        )
      )
    })
  }
))

shiny::reactlogShow()

如图:

当然,这样写虽然简洁,但是显然比较晦涩。如果是我,还是会用显式的 observer 和 update,或者 shinyjs 直接操作 DOM,因为这样虽然笨拙,但是更好一眼看懂,方便合作。在重置逻辑上需要扩展的话也比较方便。

@nan.xiao 太谢谢啦!这样清晰了很多!用 observe 和 update 还有个好处就是可以设置 update 到别的数值,不一定是初始值。