daattali / shinyjs

đź’ˇ Easily improve the user experience of your Shiny apps in seconds
https://deanattali.com/shinyjs
Other
728 stars 119 forks source link

toggleState on downloadButton doesn't take effect #246

Closed obenno closed 2 years ago

obenno commented 2 years ago

Hi @daattali ,

I used toggleState for a downloadButton to check whether the file required exists. The downloadButton is disabled as expected, but after the file is ready, the button will not be enabled immediately. I'm not quite sure if this is related to {shinyjs} or I ust wrote the code in a wrong way.

Please kindly find a reproducible example below:

library(shiny)
library(shinyjs)

ui <- fluidPage(
    shinyjs::useShinyjs(),
    div(
        textInput("filename", "File Name"),
        actionButton("go", "Generate File"),
        downloadButton("download", "Download")
    )
)

server <- function(input, output, session) {
    observe({
        toggleState(id = "download",
                    file.exists(paste0(tempdir(), "/", input$filename, ".txt"))
                    )
    })

    observeEvent(input$go, {
        req(input$filename)
        ## create file
        system(paste0("echo OK > ", tempdir(), "/", input$filename, ".txt"))
    })

    output$download <- downloadHandler(
        filename = function() {
            paste('data-', Sys.Date(), '.tgz', sep='')
        },
        content = function(file){
            system(paste0("tar cvzf ",
                          file, " ",
                          tempdir(), "/", input$filename, ".txt"))
        }
    )
}

shinyApp(ui, server)

sessionInfo:

R version 4.1.2 (2021-11-01)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.3 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0

locale:
 [1] LC_CTYPE=en_HK.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_HK.UTF-8        LC_COLLATE=en_HK.UTF-8    
 [5] LC_MONETARY=en_HK.UTF-8    LC_MESSAGES=en_HK.UTF-8   
 [7] LC_PAPER=en_HK.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_HK.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] shinyjs_2.1.0 shiny_1.7.1  

loaded via a namespace (and not attached):
 [1] compiler_4.1.2   ellipsis_0.3.2   magrittr_2.0.1   fastmap_1.1.0   
 [5] R6_2.5.1         promises_1.2.0.1 later_1.3.0      htmltools_0.5.2 
 [9] Rcpp_1.0.7       digest_0.6.29    xtable_1.8-4     httpuv_1.6.3    
[13] lifecycle_1.0.1  mime_0.12        rlang_0.4.12

Many Thanks, obenno

daattali commented 2 years ago

Your code is not a minimal reprex. A minimal reprex to test whether toggleState works is as follows:

library(shiny)
library(shinyjs)

ui <- fluidPage(
  shinyjs::useShinyjs(),
  actionButton("go", "Generate File"),
  downloadButton("download", "Download")
)

server <- function(input, output, session) {
  observe({
    # only enable the button every 3 clicks
    toggleState(id = "download", input$go %% 3 == 2)
  })
}

shinyApp(ui, server)

As you can see above, toggleState works. The issue you're having stems from incorrect code, but it's not a bug in shiny js. If you cannot solve the problem, I suggest asking for help online or reaching out to me for paid help

obenno commented 2 years ago

Thanks a lot for the confirmation, I will check it myself.

daattali commented 2 years ago

From just reading your code, without testing it myself, I think your issue here is misunderstanding how reactivity works. The observer around toggleState only gets triggered even the filename input is changed. So if you click the button and the file gets created, the observer doesn't fire. Reactivity is indeed the hardest thing to learn in shiny

obenno commented 2 years ago

Thanks for the hints, that really helps!

obenno commented 2 years ago

I've just found a solution based on shiny's reactivePoll function. In case someone else facing the same problem, just post the solution here. Thanks again!

library(shiny)
library(shinyjs)

ui <- fluidPage(
    shinyjs::useShinyjs(),
    div(
        textInput("filename", "File Name"),
        actionButton("go", "Generate File"),
        downloadButton("download", "Download")
    )
)

server <- function(input, output, session) {
    data <- reactivePoll(1000, session,
      ## This function returns the time stamp of data file
      checkFunc = function() {
        datafile <- paste0(tempdir(), "/", input$filename, ".txt")
        if (file.exists(datafile))
          file.info(datafile)$mtime[1]
        else
          ""
      },
      valueFunc = function() {
        ## do nothing
      }
    )

    observe({
        toggleState(id = "download",
                    data()
                    )
    })

    observeEvent(input$go, {
        req(input$filename)
        ## create file
        system(paste0("echo OK > ", tempdir(), "/", input$filename, ".txt"))
    })

    output$download <- downloadHandler(
        filename = function() {
            paste('data-', Sys.Date(), '.tgz', sep='')
        },
        content = function(file){
            system(paste0("tar cvzf ",
                          file, " ",
                          tempdir(), "/", input$filename, ".txt"))
        }
    )
}

shinyApp(ui, server)