Sean Slavin
Sean Slavin

Reputation: 56

In a Shiny app, how can I pause a for loop to grab a user input, then continue after a button click?

I am writing a shiny app that simulates a draft of sorts, where a user will make a selection from a list in between simulated selections (randomly chosen). To explain the premise better, we start with a list of 12 choices. The user will select one of these choices when their "slot" comes up. So if they choose '4' as their slot, the first 3 choices will be randomly simulated. Then the user will make their choice, and the final 8 choices will be randomly simulated... Anytime a selection is made, that choice is dropped from the list. So each choice can only be selected once.

So I need to loop through the numbers 1-12, and either randomly simulate that selection or (if it is the user's turn) allow the choice to be made via user input.

I have not figured out how to pause the loop until the user makes a selection, and then continue after a button is clicked to confirm. My code for the minimal, reproducible example is below. As is, the user is essentially skipped because the loop does not pause.

new_list<-c()

choice_list<-c("Choice 1","Choice 2","Choice 3","Choice 4",
               "Choice 5","Choice 6","Choice 7","Choice 8",
               "Choice 9","Choice 10","Choice 11","Choice 12")

ui<-navbarPage(title="Title", id="navbar",
  tabPanel("Main",
    selectInput(inputId="slot", label="Select your Slot", choices=1:12),
    actionButton(inputId="start", label="Start Process"),
    fluidRow(hr()),
    selectInput(inputId="user_choice", label="Choose an Option", choices=choice_list),
    actionButton(inputId="selection", label="Make Selection"))
)

server<-function(input, output, session) {
  observeEvent(input$start, {
    user_slot<-isolate(input$slot)
    print(paste0("User slot: ",user_slot))
    print("----------")
    print("----------")
    for (i in 1:12) {
      if (i==user_slot) {
        print("TIME FOR USER SELECTION")
        observeEvent(input$selection, {
          user_choice<-isolate(input$user_choice)
          print(paste0("User choice: ",user_choice))
          new_list<-c(new_list,user_choice)
          choice_list<-choice_list[choice_list!=user_choice]
        })
      } else {
        random_number<-sample(seq(1:length(choice_list)),1)
        print(paste0("Random number: ",random_number))
        random_choice<-choice_list[random_number]
        print(paste0("Random choice: ",random_choice))
        new_list<-c(new_list,random_choice)
        choice_list<-choice_list[choice_list!=random_choice]
      }
      print(paste0("# of choices left: ",length(choice_list)))
      print("----------")
    }
  })
}

shinyApp(ui=ui, server=server)

Is there a way to pause the loop, but allow it to continue when the "observeEvent()" is triggered? Is there an alternative to the "observeEvent()" that will solve this issue? Or will I have to rework a solution that has a different looping process?

Thanks in advance!

Upvotes: 0

Views: 1045

Answers (1)

Lewis Carter
Lewis Carter

Reputation: 91

I think you cannot observe an event when in a loop, I have the exact same problem and tried to pause it with Sys.sleep() and a while loop waiting for an user input that would change a value to break the loop but it does not work.

I think the easiest solution is to break you code in several parts : a function where the loop is, when it's the user's slot the loop breaks and the function returns the remaining number of choices to be made. When the user selects his choice, the loop is restarted with the remaining number of choices.

server<-function(input, output, session) {
  values <- reactiveValues(
    n = 12
  )

  observeEvent(input$selection, {
    user_choice<-isolate(input$user_choice)
    print(paste0("User choice: ",user_choice))
    new_list<-c(new_list,user_choice)
    choice_list<<-choice_list[choice_list!=user_choice]
    print(paste0("# of choices left: ",length(choice_list)))
    print("----------")

    loop(0, values$n)

  })

  observeEvent(input$start, {
    user_slot<-isolate(input$slot)
    print(paste0("User slot: ",user_slot))
    print("----------")
    print("----------")

    values$n <- loop(user_slot)
  })
}

loop <- function(user_slot, n = 12){
  for (i in 1:n) {
    if (i==user_slot) {
      print("TIME FOR USER SELECTION")
      break
    } else {
      random_number<-sample(seq(1:length(choice_list)),1)
      print(paste0("Random number: ",random_number))
      random_choice<-choice_list[random_number]
      print(paste0("Random choice: ",random_choice))
      new_list<-c(new_list,random_choice)
      choice_list<<-choice_list[choice_list!=random_choice]
    }
    print(paste0("# of choices left: ",length(choice_list)))
    print("----------")
  }
  return(n-i)
}

If you find another solution please let me know

Upvotes: 1

Related Questions