2

I am building a rudimentary shiny app. First, I created a data frame 'x' and saved it in my working directory:

x <- data.frame(a = 1:4, b = 2:5)
write.csv(x, 'x.csv', row.names = F)

In my shiny I'd like to:

  1. Upload file 'x.csv'

  2. Click my action button 'Click Here' and run a few commands upon clicking it.

  3. Get a message printed in the Shiny app itself: "Load a file!" if I click on my button "Click here" after forgetting to upload the file first.

My code works, but I can't figure out how to make my message appear.

My code:

library(shiny)

ui <- fluidPage(
  br(),
  # User should upload file x here:
  fileInput("file_x", label = h5("Upload file 'x'!")),
  br(),
  # Users clicks the button:
  actionButton("do_it", "Click Here"),
  br(),
  # Print last value of the button 'do_it':
  verbatimTextOutput("print_action")
)


server <- function(input, output, session) {

  observeEvent(input$do_it, {

    # Just a check of my button's actions:
    output$print_action <- renderPrint({input$do_it})

    # Validating the input - next 5 lines are not working:
    # validate(
    #   need(
    #     try(is.null(input$file_x), "Load a file!")
    #     )
    # )

    # Reading in the file:
    fileData <- reactive({
      infile <- input$file_x
      if (is.null(infile)) {
        return(NULL)
      }
      read.csv(infile$datapath)
    })
    x <- fileData()

    # Writing out the same file - but under a different name:
    filename <- paste0("x", input$do_it, ".csv")
    write.csv(x, file = filename, row.names = FALSE)

  })
}

shinyApp(ui, server)
Florian
  • 24,425
  • 4
  • 49
  • 80
user3245256
  • 1,842
  • 4
  • 24
  • 51
  • Do you want to disable the click here button if the cvs is not loaded? or do you want to display a specific message when you click click here? – Winicius Sabino Jan 30 '18 at 16:24
  • Actually, in this case, rather than 'hide' or 'disable' the button, I'd like to actually print a message. In my ACTUAL application I want the user to click one button that would check all his inputs (in my case it's two different data frames, and then the contents of them). And then - whenever something is wrong (when a file is not uploaded or something inside the files is wrong) - I want to return a message or several messages to the user. – user3245256 Jan 30 '18 at 16:28

1 Answers1

3

I think rather than displaying text, maybe modalDialog is better suited for what you are trying to achieve. I have implemented both solutions below, so you can compare.

Note that I also modified the reading of the csv slightly. It is bad practice to set a reactive from inside an observer. In those cases, it is better to use a reactiveVal, and update that from an observer.

Hope this helps!

library(shiny)

ui <- fluidPage(
  br(),
  # User should upload file x here:
  fileInput("file_x", label = h5("Upload file 'x'!")),
  br(),
  # Users clicks the button:
  actionButton("do_it", "Click Here"),
  br(),
  br(),
  # Print last value of the button 'do_it':
  verbatimTextOutput("print_action")
)

server <- function(input, output, session) {

  observeEvent(input$do_it, {

    if(is.null(input$file_x))
    {
      # show pop-up ...
      showModal(modalDialog(
        title = "Oh no!",
        paste0("You have not uploaded a file, silly person!"),
        easyClose = TRUE,
        footer = NULL
      ))
      # ... or update the text
      my_text('Please upload a file.')
    }
    else
    {
      # Reading in the file:
      infile <- input$file_x
      if (is.null(infile)) {
        return(NULL)
      }
      x <- read.csv(infile$datapath)
      fileData(x) # set the reactiveVal called fileData to the file inputs.

      # Writing out the same file - but under a different name:
      filename <- paste0("x", input$do_it, ".csv")
      write.csv(x, file = filename, row.names = FALSE)
      my_text('Succes!')
    }
  })

  fileData <- reactiveVal()
  my_text <- reactiveVal('')
  output$print_action <- renderText({my_text()})
}

shinyApp(ui, server)
Florian
  • 24,425
  • 4
  • 49
  • 80
  • Thank you very much, Florian! It’s very nice. May I ask a couple of questions? If my user has to upload 2 different files (via 2 different inputs) - can your code be extended like this using elseif?: if(is.null(input$file_x)){ # show pop-up …, and the rest of the code } elseif {same check but for another input file} else {rest of the code} Could you please explain again why you added fileData() in the middle of ‘server.r"? And then at the end: fileData <- reactiveVal() my_text <- reactiveVal(’’) output$printaction <- renderText ({my_text()}) Thank you! Thank you very much! – user3245256 Jan 30 '18 at 16:52
  • Extending to two files is easy. You want the pop-up when either of the pop-ups is null; so `if(is.null(input$file_x))` should be `if(is.null(input$file_x) |is.null(input$file_x2) )`. `x <- fileData()` was not necessary, I forgot to remove it. By doing `fileData(x)`, we set the `reactiveVal` equal to `x`, so we can call the contents of it from other reactives and make them dependent on it (e.g., display a table or plot). Lastly `fileData <- reactiveVal() my_text <- reactiveVal('')` initiates the reactieVal's, for more info for those see the link in my answer because I have no chars left ;) – Florian Jan 30 '18 at 16:55
  • Thank you, Florian! – user3245256 Jan 30 '18 at 17:09
  • But what if the function (under observeEvent) is more complex - for example, one reads in two files, e.g., x and y? Then one has to write: fileData(x) and fileData(y)? But how I access each of them afterwords - outside the reactive? Currently we have: fileData <- reactiveVal() - but what if there are two dataframes? – user3245256 Jan 30 '18 at 17:12
  • You can use [reactiveValues](https://shiny.rstudio.com/reference/shiny/0.11/reactiveValues.html) instead of `reactiveVal`; i.e. you can do fileData <- reactiveValues(x=NA, y = NA) to store multiple values. Note that calling and saving data to them works a bit different than for `reactiveVal`, e.g. `fileData$x <- 3` to store values and `fileData$x` to retrieve. Hope this clarifies! – Florian Jan 30 '18 at 19:11