MarkeD
MarkeD

Reputation: 2631

Shiny Modules: how to access the input$ of a looped Shiny Module?

I'm wrapping my head around Shiny Module nesting and have this problem.

The code for an example Shiny app with the issue is available here. Copy-pasting into an .R file and running in RStudio should replicate it.

I'm trying to make a helper function that will filter/aggregate any data.frame. I am as far as getting some dynamic SelectInputs which is already useful, but I now want to put those SelectInputs into a loop so I can subset a data.frame.

The problem seems to be that the inputs that are generated within the Shiny server loop can not be accessed via normal Shiny modules means, and the syntax for finding their names eludes me. The issue lies within these lines:

  new_data <- reactive({
    old_data <- the_data()
    for(i in seq_along(aggs)){

      str(i) ## debug to check its in loop

      agg <- aggs[i]

      ## How to access the dynamic_select inputs selected?
      inputA <- input[[agg]]  # is NULL?
      old_col <- old_data[[agg]] 

      str(inputA) ## debug - NULL should hold values of dynamic inputs

      new_data <- old_data[inputA %in% old_col,]

      old_data <- new_data

    }

    new_data

  })

Does anyone have an idea on how to access the selected values that should be appearing in variable inputA ?

Upvotes: 1

Views: 2328

Answers (2)

MarkeD
MarkeD

Reputation: 2631

Joe helped in the Shiny Google Group, the Gist now has a working version: https://gist.github.com/MarkEdmondson1234/7565787bb020b1c7cb691cf80e816d68

Here was his answer:

The general rule for modules is that you can only access the value of a moduleUI's input, in the corresponding module server function. In this case, you can only access the value of dynamicSelectInput from within dynamicSelect. If you want to then make that value available to other modules, then you have to return it as a reactive from the module server function. In this case, that means that the dynamicSelect function should end with this line:

  return(reactive(input$dynamic_select))

("return" is optional but I think it helps make explicit the fact that reactive(input$dynamic_select) is the return value.)

Then, in outerTable, you could do something like this instead of your for-loop:

  selectResults <- lapply(setNames(aggs, aggs), function(agg) {
    callModule(module = dynamicSelect,
      id = agg,
      the_data = the_data,
      column = agg)
  })

Now selectResults is a named list, each element being a reactive expression (so selectResults[[agg]]() to retrieve a value).

Upvotes: 4

user2894965
user2894965

Reputation: 96

This feels quite "hacky", but if you change your new_data reactive to

  new_data <- reactive({
    old_data <- the_data()
    for(i in seq_along(aggs)){
      str(i) ## debug to check its in loop
      agg <- aggs[i]
      inputA <- input[[paste0(aggs[i], '-dynamic_select')]]
      old_col <- old_data[[agg]]
      new_data <- old_data[inputA %in% old_col,]
      old_data <- new_data
    }
    new_data
  })

then you should get an output.

What seems to be happening here is your dynamic inputs elements have been put through the ns() mill twice (and necessarily so), so the elements on the webpage are called "debug_dynamic-mpg-dynamic_select" and "debug_dynamic-gear-dynamic_select" By hardcoding the "-dynamic_select" in the new_data reactive, you'll make it work. But the cost is that if you change "dynamic-select" in your dynamicSelectUI, you'll also need to remember to change it in your new_data function. The code is not DRY, but that can possibly be fixed with some variable somewhere. I'll leave that headache to you.

Cheers Your friend in Edinburgh

Upvotes: 0

Related Questions