pass renderUI input from one Shiny module to another

前端 未结 1 1444
清酒与你
清酒与你 2021-01-22 11:53

I am trying to modularize Shiny code, for uploading CSV file as input into scatterD3 plot. Additional UI control will be from renderUI to change the x-variable and y-variable. I

相关标签:
1条回答
  • 2021-01-22 12:32

    Okay, this was indeed a bit difficult, as working with modules is not exactly straightforward. You were close... your main problem was not packing up all of the reactives in a list and passing them to where they were needed.

    I made the following changes:

    1. csvFile: declared additional reactive functions xvar and yvar in the csvFile server module function similarly to what you had already done for dataframe.
    2. csvFile: packed all the needed reactives up as a list and returned it as the return value as described in the design pattern link in your post. (Thank you Steph Locke).
    3. server: passed that list down in the callModule(D3scatter,... ), again as described in that link.
    4. D3scatter: refactored a bit by making the call to scatterD3 to use vectors extracted from the specified dataframe. This is because I couldn't get it to work with strings as column specifiers (but there is surely a way somehow).

    Here are the changed code parts from above:

    csvFile server module

    csvFile <- function(input, output, session, stringsAsFactors) {
      ns <- session$ns
      ## to reuse namespace, session must be first!!!
    
      ## User selected file
      userFile <- reactive({
        # If no file is selected, don't do anything
        validate(need(input$file, message = FALSE))
        input$file
      })
    
      dataframe <- reactive({
        read.csv(
          userFile()$datapath,
          header = input$header,
          sep=input$sep,
          quote = input$quote,
          stringsAsFactors = stringsAsFactors
        )
      })
      # We can run observers in here if we want to
      observe({
        msg <- sprintf("File %s was uploaded", userFile()$name)
        cat(msg, "\n")
      })
    
      xvar <- reactive({input[[ "xvar" ]] })
      yvar <- reactive({input[[ "yvar" ]] })
    
      output$controls <- renderUI({
        ## use taglist to keep everything together
        tagList(
          fileInput(ns('file'), 'Choose CSV file', 
                    accept=c('txt/csv','text/comma-separated-values,text/plain','.csv')),
          checkboxInput(ns('header'), 'Has heading', TRUE),
          radioButtons(ns('sep'),'Separator', c(Comma=',',Semicolon=';',Tab='\t'), ','),
          selectInput(ns('quote'),'Quote', c(None ='','Double Quote'='"','Single Quote'="'"),'"')
        )
      })
    
      ## use renderUI to display table
      output$csvTable <- renderUI({
        output$table <- renderDataTable(dataframe())
        dataTableOutput(ns("table"))
      })
    
      ## Column Name
      output$ColName <- renderUI({
        df <- dataframe()
        if (is.null(df)) return(NULL)
        items=names(df)
        print(items)
        names(items)=items
        tagList(
          selectInput(ns("xvar"), "Column Names", items),
          selectInput(ns("yvar"), "Column Names", items)
        )
      })
    
      ## Column Entry
      output$ColEntry <- renderUI({
        df <- dataframe()
        if (is.null(input$col)) return(NULL)
        tagList(
          selectInput(ns("entry"), "Entry Names", df[,input$xvar])
        )
      })
    
      rlist <- list(dataframe=dataframe,xvar=xvar,yvar=yvar)
      # Return the reactive that yields the data frame
      return(rlist)
    
    }## End of module
    

    server

    server <- function(input, output, session) {
      ## Option 1. CSV uploaded file
      rlist <- callModule(csvFile, "basic", stringsAsFactors = FALSE) 
    
      ## Option 2. mtcar data loaded at start
      #datafile <- reactive({mtcars}) ## data loaded at runApp()
      #callModule(csvFile, "basic") 
    
      callModule(D3scatter, "first", rlist)
    
    }
    

    D3scatter

    D3scatter <- function(input,output,session,rlist){
      ns <- session$ns
    
      output$scatterplot1 <- renderScatterD3({
        #scatterD3(data = data, x=mpg, y=carb,
        mtdf <- rlist$dataframe()
        x <- mtdf[[rlist$xvar()]]
        y <- mtdf[[rlist$yvar()]]
        scatterD3(x=x,y=y,
                  labels_size= 9, point_opacity = 1,
                  #col_var=cyl, symbol_var= data$Assay,
                  #lab= paste(mpg, carb, sep="|") , lasso=TRUE,
                  #xlab= "IFN-γ", ylab= "IL-10",
                  #click_callback = "function(id, index) {
                  #  alert('scatterplot ID: ' + id + ' - Point index: ' + index) 
                  #  }", 
                  transitions= T)
      })
    }
    

    Then it worked:

    Here is all the running code again, in case I forgot a change somewhere, or someone just wants to run it. As an aside it is quite cool the way the scatter plot changes from one plot to another... it morphs continuously with an animation-like effect. Unusual.

    Entire application in one file

    ## load libraries
    library(shiny)
    library(stringr)
    library(scatterD3)
    
    #source("/Users/echang/scratch/tmp/MSD_D3scatter/csvFile_Module.R")
    csvFileInput <- function(id, label="CSV file") {
      ## Create namespace
      ns<-NS(id)
      tagList(
        uiOutput(ns("controls"))
      )
    }
    
    csvFileControl <- function(id){
      ns <- NS(id)
      tagList(
        column(width=3, uiOutput(ns("ColName"))),
        column(width=3, uiOutput(ns("ColEntry")))
      )
    }
    
    csvFileUI <- function(id){
      ns <- NS(id)
      tagList(
        uiOutput(ns("csvTable"))
      )
    }
    
    ## server module
    csvFile <- function(input, output, session, stringsAsFactors) {
      ns <- session$ns
      ## to reuse namespace, session must be first!!!
    
      ## User selected file
      userFile <- reactive({
        # If no file is selected, don't do anything
        validate(need(input$file, message = FALSE))
        input$file
      })
    
      dataframe <- reactive({
        read.csv(
          userFile()$datapath,
          header = input$header,
          sep=input$sep,
          quote = input$quote,
          stringsAsFactors = stringsAsFactors
        )
      })
      # We can run observers in here if we want to
      observe({
        msg <- sprintf("File %s was uploaded", userFile()$name)
        cat(msg, "\n")
      })
    
      xvar <- reactive({input[[ "xvar" ]] })
      yvar <- reactive({input[[ "yvar" ]] })
    
      output$controls <- renderUI({
        ## use taglist to keep everything together
        tagList(
          fileInput(ns('file'), 'Choose CSV file', 
                    accept=c('txt/csv','text/comma-separated-values,text/plain','.csv')),
          checkboxInput(ns('header'), 'Has heading', TRUE),
          radioButtons(ns('sep'),'Separator', c(Comma=',',Semicolon=';',Tab='\t'), ','),
          selectInput(ns('quote'),'Quote', c(None ='','Double Quote'='"','Single Quote'="'"),'"')
        )
      })
    
      ## use renderUI to display table
      output$csvTable <- renderUI({
        output$table <- renderDataTable(dataframe())
        dataTableOutput(ns("table"))
      })
    
      ## Column Name
      output$ColName <- renderUI({
        df <- dataframe()
        if (is.null(df)) return(NULL)
        items=names(df)
        print(items)
        names(items)=items
        tagList(
          selectInput(ns("xvar"), "Column Names", items),
          selectInput(ns("yvar"), "Column Names", items)
        )
      })
    
      ## Column Entry
      output$ColEntry <- renderUI({
        df <- dataframe()
        if (is.null(input$col)) return(NULL)
        tagList(
          selectInput(ns("entry"), "Entry Names", df[,input$xvar])
        )
      })
    
      rlist <- list(dataframe=dataframe,xvar=xvar,yvar=yvar)
      # Return the reactive that yields the data frame
      return(rlist)
    
    }## End of module
    
    
    ## scatterD3 module -------------------------------------------------------------
    
    D3scatterUI <- function(id){
      ns<-NS(id)
      tagList(
        scatterD3Output(ns("scatterplot1"))
      )
    }
    
    D3scatter <- function(input,output,session,rlist){
      ns <- session$ns
    
      output$scatterplot1 <- renderScatterD3({
        #scatterD3(data = data, x=mpg, y=carb,
        mtdf <- rlist$dataframe()
        x <- mtdf[[rlist$xvar()]]
        y <- mtdf[[rlist$yvar()]]
        scatterD3(x=x,y=y,
                  labels_size= 9, point_opacity = 1,
                  #col_var=cyl, symbol_var= data$Assay,
                  #lab= paste(mpg, carb, sep="|") , lasso=TRUE,
                  #xlab= "IFN-γ", ylab= "IL-10",
                  #click_callback = "function(id, index) {
                  #  alert('scatterplot ID: ' + id + ' - Point index: ' + index) 
                  #  }", 
                  transitions= T)
      })
    }
    
    
    ## Shiny ######################################################################
    ui <- fluidPage(
      titlePanel("Upload"),
    
      tabsetPanel(type="tabs",
                  tabPanel("tab1",
                           sidebarLayout(
                             sidebarPanel(csvFileInput("basic")),
                             mainPanel(csvFileUI("basic"))
                           )
                  ),
                  tabPanel("tab2",
                           tagList(
                             fluidRow(csvFileControl("basic")),
                             fluidRow(D3scatterUI("first"))
                           )
                  )
      )
    )
    
    server <- function(input, output, session) {
      ## Option 1. CSV uploaded file
      rlist <- callModule(csvFile, "basic", stringsAsFactors = FALSE) 
    
      ## Option 2. mtcar data loaded at start
      #datafile <- reactive({mtcars}) ## data loaded at runApp()
      #callModule(csvFile, "basic") 
    
      callModule(D3scatter, "first", rlist)
    
    }
    
    shinyApp(ui, server)
    
    0 讨论(0)
提交回复
热议问题