Отобразить выбранный вариант динамически сгенерированного виджета selectizeInput на основе выбранного варианта предыдущего виджета

Я пытаюсь создать приложение Shiny, которое генерирует строки с несколькими виджетами selectInput или selectizeInput, когда пользователь нажимает кнопку «+», и удаляет такие строки, когда он нажимает кнопку «-». На изображении ниже показано, чего я достиг с этого момента:

Как сейчас выглядит мое блестящее приложение: https://imgur.com/uQfdJrv

Моя проблема в том, что я не могу найти способ показать виджеты в новой строке с теми же выбранными значениями виджета в предыдущей строке. На самом деле пользователь может захотеть изменить только значения одних входных данных и не трогать значения других.

Ссылаясь на изображение выше, давайте предположим, что для первого теста пользователь выбрал «Амикацин» в качестве антибиотиков и «5» в качестве УПП. Второй тест всегда с «Амикацином», но он получил значение AMR 10. Новые входы, сразу после нажатия кнопки «+», должны отображать сразу «Амикацин» и «5», и после этого пользователь просто изменит «5» второго ряда в «10».

Вот код, который я использовал:


library(shiny)
library(shinyjs)

###= UI
ui <- fluidPage(

br(),

useShinyjs(),

  fluidRow(
    column(width = 12,
           actionButton(inputId = "add_amr_test",
                        label = icon(name = "plus",
                                     lib = "font-awesome")),
           actionButton(inputId = "remove_amr_test",
                        label = icon(name = "minus",
                                     lib = "font-awesome")),
           div(style = "display: inline-block;
                        padding: 0px 10px;",
               h5("Add or remove an antimicrobial resistance test")),
           tags$div(id = "amr_test_placeholder")
    )
  ),

br()

)

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

  Antibiotics_name <- c("", "Amikacin", "Ampicillin", "Tetracycline")

  observe({

    toggleState(id = "remove_amr_test",
                condition = input$add_amr_test > input$remove_amr_test)

  })

  amr_test_values <- reactiveValues(val = 0)

  ###= Add ui
  observeEvent(input$add_amr_test, {

    amr_test_divId <- length(amr_test_values$val) + 1

    insertUI(
      selector = "#amr_test_placeholder",
      where = "beforeBegin",
      ui = tags$div(
        id = amr_test_divId,
        fluidRow(

          br(),

          column(width = 3,
                 selectizeInput(inputId = paste0("drug_",
                                                 input$add_amr_test - input$remove_amr_test),
                                label = h5(paste0("Antibiotic ",
                                                  input$add_amr_test - input$remove_amr_test)),
                                choices = Antibiotics_name,
                                selected = "")
          ),
          column(width = 1,
                 selectizeInput(inputId = paste0("rescom_",
                                                 input$add_amr_test - input$remove_amr_test),
                                label = h5(paste0("AMR ",
                                                  input$add_amr_test - input$remove_amr_test)),
                                choices = c("",
                                            seq(from = 1,
                                                to = 100,
                                                by = 1)),
                                selected = "")
          )
        )
      )
    )

    amr_test_values$val <- c(amr_test_values$val,
                             amr_test_divId)

  })

  ###= Remove ui
  observeEvent(input$remove_amr_test, {

    removeUI(

      selector = paste0('#', amr_test_values$val[length(amr_test_values$val)])

    )

    amr_test_values$val <- amr_test_values$val[-length(amr_test_values$val)]

  })

}



###= RUN APP
shinyApp(ui = ui, server = server)

Наконец, вот изображение с ожидаемыми результатами, полученными после того, как пользователь нажмет кнопку «+» для создания второго ряда виджетов.

Пример ожидаемого результата: https://imgur.com/NIpOZOE

И последний вопрос: как только каждый виджет от пользователя имеет определенное значение, как лучше всего хранить все эти значения внутри фрейма данных?

Большое спасибо.


person nd091680    schedule 05.08.2019    source источник
comment
Думал, вас может заинтересовать этот вопрос, и ответьте stackoverflow.com/questions/57571196/   -  person Eli Berkow    schedule 20.08.2019
comment
Большое спасибо Эли, я постараюсь понять, подходит ли эта ситуация для моего случая! Вы очень любезны!   -  person nd091680    schedule 20.08.2019


Ответы (1)


Пожалуйста, смотрите ниже для вашего первого вопроса:

library(shiny)
library(shinyjs)

###= UI
ui <- fluidPage(

br(),

useShinyjs(),

  fluidRow(
    column(width = 12,
           actionButton(inputId = "add_amr_test",
                        label = icon(name = "plus",
                                     lib = "font-awesome")),
           actionButton(inputId = "remove_amr_test",
                        label = icon(name = "minus",
                                     lib = "font-awesome")),
           div(style = "display: inline-block;
                        padding: 0px 10px;",
               h5("Add or remove an antimicrobial resistance test")),
           tags$div(id = "amr_test_placeholder")
    )
  ),

br()

)

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

  Antibiotics_name <- c("", "Amikacin", "Ampicillin", "Tetracycline")

  observe({

    toggleState(id = "remove_amr_test",
                condition = input$add_amr_test > input$remove_amr_test)

  })

  amr_test_values <- reactiveValues(val = 0)

  ###= Add ui
  observeEvent(input$add_amr_test, {

    amr_test_divId <- length(amr_test_values$val) + 1

    # Simplified input_number here and code to obtain previous values if they exist
    input_number <- input$add_amr_test - input$remove_amr_test
    if (!is.null(eval(parse(text = paste0("input$drug_", input_number - 1))))) {
        drug_value = eval(parse(text = paste0("input$drug_", input_number - 1)))
    } else {
        drug_value = ""
    }
    if (!is.null(eval(parse(text = paste0("input$rescom_", input_number - 1))))) {
        rescom_value = eval(parse(text = paste0("input$rescom_", input_number - 1)))
    } else {
        rescom_value = ""
    }

    insertUI(
      selector = "#amr_test_placeholder",
      where = "beforeBegin",
      ui = tags$div(
        id = amr_test_divId,
        fluidRow(

          br(),

          column(width = 3,
                 selectizeInput(inputId = paste0("drug_",
                                                 input_number),
                                label = h5(paste0("Antibiotic ",
                                                  input_number)),
                                choices = Antibiotics_name,
                                selected = drug_value)
          ),
          column(width = 1,
                 selectizeInput(inputId = paste0("rescom_",
                                                 input_number),
                                label = h5(paste0("AMR ",
                                                  input_number)),
                                choices = c("",
                                            seq(from = 1,
                                                to = 100,
                                                by = 1)),
                                selected = rescom_value)
          )
        )
      )
    )

    amr_test_values$val <- c(amr_test_values$val,
                             amr_test_divId)

  })

  ###= Remove ui
  observeEvent(input$remove_amr_test, {

    removeUI(

      selector = paste0('#', amr_test_values$val[length(amr_test_values$val)])

    )

    amr_test_values$val <- amr_test_values$val[-length(amr_test_values$val)]

  })

}



###= RUN APP
shinyApp(ui = ui, server = server)
person Eli Berkow    schedule 05.08.2019
comment
Эли Беркоу, большое спасибо за ваш быстрый ответ и за ваше простое, элегантное, а также очень умное решение, оно полностью работает. Надеюсь, это будет полезно и для других. Что касается моего второго вопроса, я затем сохранил данные в кадре данных, применив sapply к каждой переменной, для которой я получил ввод таким образом: input[[paste0("variable_", i)]]. - person nd091680; 05.08.2019
comment
Я рад, что это сработало. Звучит как умный способ хранения данных. Я немного рассмотрел этот вопрос, но мне показалось довольно сложным попытаться отслеживать входные данные и все их обновления. - person Eli Berkow; 06.08.2019
comment
Эли, так же, как вы создали разные inputId, как лучше всего настроить новые виджеты с помощью CSS? Например, для selectizeInputs Drug_i я пытался добавить (сверху) этот код: tags$style(paste0("#animal_species_", input_number(), " {font-size:1px; width:200px; height:5px;}")), но, похоже, он не работает. Я не могу указать общий тег для каждого виджета, потому что они должны быть разными. Спасибо еще раз. - person nd091680; 06.08.2019
comment
Это совсем другой вопрос, и его, возможно, стоит создать отдельно на SO. Пожалуйста, дайте мне точные требования. Должен ли препарат отличаться от рескома? Индивидуальный препарат/реском? - person Eli Berkow; 06.08.2019
comment
Да Эли, но мне удалось найти решение. Обернув каждый столбец (с множественным динамическим вводом для ОДНОЙ переменной) внутри div(), я изменил стиль всего виджета selectizeInput только для этого div(), и это сработало. Большое спасибо за проявленный интерес и оперативные ответы! - person nd091680; 06.08.2019
comment
Большой!. Удачи - person Eli Berkow; 06.08.2019
comment
Эли Берков Я добавил новый вопрос, связанный с этой темой здесь. Заранее спасибо за доступность. - person nd091680; 08.08.2019
comment
Привет, Эли, есть предложения, как изменить код, чтобы первая строка автоматически отображалась, когда пользователь открывает форму? - person nd091680; 13.08.2019
comment
Первая строка с пустыми значениями или заполнена? - person Eli Berkow; 14.08.2019
comment
Вы можете добавить observeEvent(input$add_amr_test, {...}, ignoreNULL = FALSE), но мне нужно точно знать, что вы хотите после этого. Вы также можете добавить первую строку в свой пользовательский интерфейс. Его тоже можно удалить или нет? - person Eli Berkow; 14.08.2019
comment
Или в основном при запуске приложения должна быть только первая пустая строка (например, что произойдет, если вы впервые нажмете кнопку +). Эта строка пуста, как и другие, но после ее заполнения все следует скопировать снова, как в вашем ответе. Не знаю, нужно ли его удалять, может быть, нет. Что вы думаете? Должен ли я создать еще один вопрос, на который вы могли бы ответить лучше? - person nd091680; 14.08.2019
comment
Вы пробовали ignoreNULL = FALSE выше? - person Eli Berkow; 14.08.2019
comment
Только что попробовал, первая строка отображается хорошо, но счет начинается с 0, а кнопка удаления больше не удаляет строки. Должен ли я также изменить что-то в другом наблюдателе? - person nd091680; 14.08.2019
comment
также отредактируйте condition = input$add_amr_test >= input$remove_amr_test (›=) и отредактируйте input_number <- input$add_amr_test - input$remove_amr_test + 1 - person Eli Berkow; 14.08.2019
comment
Также кнопка удаления просто не позволит удалить первую строку - она ​​удалит другие. Поэкспериментируйте с › и ›= для этого, если вы хотите, чтобы первая строка не удалялась. - person Eli Berkow; 14.08.2019
comment
Эли, извините за мой предыдущий комментарий, я сделал ошибку, поместив идентификатор amr_div в кавычки (amr_test_divId), и поэтому кнопка удаления больше не работает. Ваше решение блестяще. Большое спасибо. - person nd091680; 14.08.2019