Dianafreedom
Dianafreedom

Reputation: 411

using renderUI to update a data set instead of conditional panel

I am writing a shiny app to realize the following effects:

Whenever I choose variable included by categoryname, the web will generate the slider which provides a divider. It divides the selected variable into 2 groups and form a new column containing the group name added to the original data set.

People here helped me solve the problem using conditional panel in this question but now I use renderUI combined with shinyjs cause conditional panel doesn't work in my large project.

I am stuck by a tiny bug (seems):

Error in data.frame: arguments imply differing number of rows: 32, 0

The following is my code, how to change it so that the function works?

library(shiny)
library(shinyjs)
library(stringr)

categoryname = c("mpg_group", "disp_group")
MT_EG = mtcars[,1:5]

# Define UI for application that draws a histogram
ui <- fluidPage(

  useShinyjs(),

  # Application title
  titlePanel("Mtcars Data"),

  # Sidebar with a slider input for number of bins 
  sidebarLayout(
    sidebarPanel(
      selectInput(inputId = "arm",
                  label = "ARM VARIABLE",
                  choices = c("mpg_group", "cyl", "disp_group", "hp", "drat"),
                  selected = "cyl"),
      # conditionalPanel(
      #   #condition = "categoryname.includes(input.arm)",
      #   condition = "input.arm == 'disp_group' | input.arm == 'mpg_group'",
      #   
      #   #sliderInput("divider", "divide slider", 1, 100, 20)
      #   optionalSliderInputValMinMax("divider", "divide slider", c(50,0,100), ticks = FALSE)
      # )
      uiOutput("divider")
    ),

    # Show a plot of the generated distribution
    mainPanel(
      uiOutput("data")
    )
  )
)

# Define server logic required to draw a histogram
server <- function(input, output, session) {

  output$divider <- renderUI({
    if (input$arm %in% categoryname){
      show("divider")
    }
    else{
      hide("divider")
    }
    sliderInput("divider", "divide slider", 0, 100, 50)
  })

  observeEvent(
    input$arm,
    observe(
      {
        if (input$arm %in% categoryname){
          #browser()
          # start over and remove the former column if exists
          MT_EG = MT_EG[, !(colnames(MT_EG) %in% input$arm)]

          id_arm_var <- input$arm
          id_arm <- unlist(str_split(id_arm_var,'_'))[1]


          # change the range of the slider
          val <- input$divider
          mx = max(MT_EG[[id_arm]])
          mn = min(MT_EG[[id_arm]])
          updateSliderInput(session, inputId = "divider", min=floor(mn/2),max = mx + 4,step = 1,value = input$divider)

          # generate a new column and bind
          divi <- data.frame(id_arm_var = MT_EG[[id_arm]]>input$divider)
          divi$id_arm_var[divi$id_arm_var==TRUE] <- paste0(id_arm_var, " Larger")
          divi$id_arm_var[divi$id_arm_var==FALSE] <- paste0(id_arm_var, " Smaller")
          colnames(divi) <- id_arm_var
          MT_EG <- cbind(MT_EG,divi)
        }

        output$data=renderTable(MT_EG)
      }
    )
  )
}

# Run the application 
shinyApp(ui = ui, server = server)

I simply use mtcars dataset so that all of you can get access to

Upvotes: 4

Views: 181

Answers (1)

A. Suliman
A. Suliman

Reputation: 13135

observeEvent and renderUI both depend on "or triggered by" input$arm. What happened is observeEvent ask for input$divider before renderUI rendered probably in the UI and input$divider becomes available for observeEvent which as I mentioned above input$divider ends up with value NULL.

To solve this problem just add req(input$divider) before MT_EG = MT_EG[, !(colnames(MT_EG) %in% input$arm)]. Also change output$divider to output$dividerUI since Shiny doesn't allow input and output with the same id.

See ?shiny::req for more details.

Upvotes: 1

Related Questions