1

First of all, I am no expert of Shiny and there are many functions I don't really understand. So please, be patient with me. I found some other topics (this and this) discussing the same problem but I am not sure how to apply the solution to mine.

I got two selectizeInput(), one called Run and the other Sample. I have the function written for Sample to be dependent of Run. Which means that if I select some inputs from Run, it will filter results of Sample. Here's the code below.

library(shiny)
library(tidyverse)
library(shinydashboard)

get_some_data <- structure(list(Sample = c("ADAWD", "23016", "11384"),
                                Flowcell_Line = c("1,2", "1", "1"),
                                Run = c("Run_399", "Run_399_auto2", "Run_399_auto2"),
                                Date = structure(c(1569836460, 1569836460, 1569836460),
                                                 class = c("POSIXct", "POSIXt"))), 
                           row.names = c(NA, 3L), class = "data.frame")

ui <- fluidPage(fluidRow(
    box(selectizeInput("run", "Run",
        choices = unique(get_some_data[["Run"]]),
        multiple = TRUE,
        options = list(placeholder = "Enter a Run ID...")
    )),
    box(shiny::uiOutput("ui_sample"))
))

server <- function(input, output, session)
    ({
        output$ui_sample <- renderUI(if (is.null(input$run)) {
            selectizeInput(
                inputId = 'sample',
                'Sample',
                choices = unique(get_some_data[["Sample"]]),
                multiple = TRUE,
                options = list(placeholder = 'Enter a Sample ID...')
            )
        } else {
            selectizeInput(
                inputId = 'sample',
                'Sample',
                choices = unique(filter(get_some_data, Run %in% input$run)[["Sample"]]),
                multiple = TRUE,
                options = list(placeholder = 'Enter a Sample ID...')
            )
        })
    })

shinyApp(ui = ui, server = server)

Now, I wish to make it reversible. By that, I mean that if I type something in the Sample box, the Run box will be filtered. But I am not able to make the inter-dependency because it creates a loop that actualize the inputs. This is what I tried:

ui <- fluidPage(fluidRow(
    box(shiny::uiOutput("ui_run")),
    box(shiny::uiOutput("ui_sample"))
))

and adding to server:

        output$ui_run <- renderUI(
            if (is.null(input$sample)) {
                selectizeInput(
                    inputId = 'run',
                    'Run',
                    choices = unique(get_some_data[["Run"]]),
                    multiple = TRUE,
                    options = list(placeholder = 'Enter a Run ID...')
                )
            } else {
                selectizeInput(
                    inputId = 'run',
                    'Run',
                    choices = unique(filter(
                        get_some_data, Sample %in% input$sample
                    )[["Run"]]),
                    multiple = TRUE,
                    options = list(placeholder = 'Enter a Run ID...')
                )
            })

I feel like there is an important function I am missing that I don't know about... can you please help me figure it out ?

thanks in advance.

user324810
  • 597
  • 8
  • 20
  • I don't think you've been missing an important dedicated function. Such circular dependencies are always tricky, and I do not know of a systematic way to deal with them. – Aurèle Oct 04 '19 at 12:47
  • thanks @Aurèle. Well, I will try to figure out something, will come back post here if I get anything – user324810 Oct 04 '19 at 12:54
  • Pointers: you'll be better off using `updateSelectizeInput` rather than `renderUI`. Also, have a very clear definition of desired behavior before implementing (e.g.: what should happen when A has existing selections and B is updated, should it reset everything or not, etc...) – Aurèle Oct 04 '19 at 13:09
  • @Aurèle yeah I saw the `updateSelectizeInput` though I am not sure what it exactly does. I will do some tests with it. Concerning the desired behavior, it is as your example, if A has existing selections and B is updated (or vice-versa), nothing should reset. Unless of course, I delete all inputs – user324810 Oct 04 '19 at 13:20

1 Answers1

1
ui <- fluidPage(fluidRow(
  box(selectizeInput("run", "Run",
                     choices = unique(get_some_data[["Run"]]),
                     multiple = TRUE,
                     options = list(placeholder = "Enter a Run ID..."))),
  box(selectizeInput("sample", "Sample", 
                     choices = unique(get_some_data[["Sample"]]), 
                     multiple = TRUE,
                     options = list(placeholder = 'Enter a Sample ID...')))
))

server <- function(input, output, session) ({
  observeEvent(input$run, {
    updateSelectizeInput(
      session, "sample",
      choices = if (is.null(input$run)) unique(get_some_data[["Sample"]]) else
        unique(filter(get_some_data, Run %in% input$run)[["Sample"]]),
      selected = input$sample
    )
  }, ignoreNULL = FALSE)

  observeEvent(input$sample, {
    updateSelectizeInput(
      session, "run",
      choices = if (is.null(input$sample)) unique(get_some_data[["Run"]]) else
        unique(filter(get_some_data, Sample %in% input$sample)[["Run"]]),
      selected = input$run
    )
  }, ignoreNULL = FALSE)
})

shinyApp(ui = ui, server = server)
Aurèle
  • 12,545
  • 1
  • 31
  • 49