tellyshia
tellyshia

Reputation: 11

How to call shiny module based on reactive input?

I would like to call a nested module server based on a reactive input and display the return output on the main panel. The function callModule doesn't seem to work here when it is depending on a reactive input.

I assume this to be a common scenario when you’re building a complex app but I’ve been struggling to find a solution.

Below is my code or you can run the app by entering the following command in R:

shiny::runGist("23abbb50a1df6a91af990a7401919044")

Thank you in advance.

library(shiny)

# innerModule 1
csvFile_Input <- function(id, label = ".csv file") {
  # Create a namespace function using the provided id
  ns <- NS(id)
  tagList(
    fileInput(ns("file"), label),
    checkboxInput(ns("heading"), "Has heading")
  )
}


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


  userFile <- reactive({
    req(input$file)
  })

  dataframe <- reactive({

    read.csv(userFile()$datapath,
             header = input$heading)
  })

  observe({
    msg <- sprintf("File %s was uploaded", userFile()$name)
    cat(msg, "\n")
  })



  return(dataframe)

}

# innerModule 2
othersource_Input <- function(id) {
  ns <- NS(id)
  textInput("text", label = h3("From the web"), value = "http://www.stats.ox.ac.uk/pub/datasets/csb/ch11b.dat")
}

othersource <- function(input, output, session){
  library(data.table)

  dataframe <- reactive({
    req(input$text)
    fread(input$text)
  })

   return(dataframe)
}

loadDataInput <- function(id){
  ns <- NS(id)
  wellPanel(
    tagList(
      uiOutput(ns("selectSource")),
      uiOutput(ns("text"))
    )
  )


}



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

  ns <- session$ns

  # Select source from user or from wibi
  output$selectSource<-renderUI(
    radioButtons(
      ns("dt"), "Data source",
      c(csvFile = "fromCSV", web = "fromWeb"),
      inline = TRUE
    )
  )

  dtype <- reactive({
    req(input$dt)
  })


  output$text <- renderUI({
    req(dtype())
    if(dtype() == "fromCSV"){
      csvFile_Input(ns("user"))
    } else if (dtype() == "fromWeb"){
      othersource_Input(ns("web"))
    }
  })


 data <- reactive({
    req(dtype())
    if(dtype() == "fromCSV"){
      callModule(csvFile, "user")
    } else if (dtype() == "fromWeb"){
      callModule(othersource, "web")
    }
  })


  output$viewinput <- renderPrint({
    print(data())
  })


  return(data)

}

loadDataOutput <- function(id){
  ns <- NS(id)
  verbatimTextOutput(ns("viewinput"))
}


ui <- fluidPage(
  titlePanel(paste("Calling module based on reactive input")),
  sidebarLayout(
    sidebarPanel(
      loadDataInput("test")
    ),
    mainPanel(
      loadDataOutput("test")
    )
  )
)

server <- function(input, output) {
  callModule(loadData, "test")
}

shiny::shinyApp(ui = ui, server = server)

Upvotes: 1

Views: 1049

Answers (1)

Gregor de Cillia
Gregor de Cillia

Reputation: 7695

You shouldn't invoke callModule inside renderXXX or reactive functions. The following changes worked for me.

csvinput <- callModule(csvFile, "user")
otherinput <- callModule(othersource, "web")

data <- reactive({
  req(dtype())
  if(dtype() == "fromCSV"){
    csvinput()
  } else if (dtype() == "fromWeb"){
    otherinput()
  }
})

Also, you should use ns("text") in othersource_Input. If you want further explanations, please provide a minimal example.

Upvotes: 2

Related Questions