R Shiny - создание закладок для динамически создаваемого пользовательского интерфейса

У меня возникла небольшая проблема (возможно, ошибка?) С закладками Shiny - я создаю динамический интерфейс - новые текстовые и числовые поля. Эти входы создаются нажатием кнопки ActionButton. При сохранении URL-адреса закладки - значения числового и текстового ввода находятся в этом URL-адресе. Все идет нормально. Однако после загрузки сохраненного URL-адреса я вижу только первый динамически созданный пользовательский интерфейс. Мне нужно нажать кнопку действия, чтобы добавить следующие числовые и текстовые поля. Значения сохраняются в URL-адресе, и после добавления этих входных данных они заполняются правильными сохраненными значениями. Но это немного неудобно, если у вас есть 20 таких кнопок, и вам нужно щелкнуть 19 раз, чтобы все они оказались на экране. Вот короткий воспроизводимый пример.

library(shiny)

ui <- function(request){
  shinyUI(fluidPage(
    bookmarkButton(),
    sidebarLayout(
      actionButton('addElement', 'Add Element', icon("plus"), class="btn-success text-white"),
    mainPanel(
        id ="content"
      )
    )
  ))
}


shinyServer(function(input, output) {
  enableBookmarking("url")
  countvar <<- 0
  observeEvent(input$addElement, {
    countvar <<- countvar + 1
    element <- paste0("var", countvar)
    insertUI(
      selector = "#content",
      where = "beforeEnd",
      ui = tagList(
        wellPanel(
          style = "display:-webkit-inline-box;width:100%",
          id = element,
          column(3,
                 textInput(element, "Element name")
          ),
          column(3,
                 numericInput(paste0(element, "Value"), "Element Value", NULL)
          )
        )
      )
    )
  })

})

Я нашел аналогичный вопрос по SO, к сожалению, без ответа - Блестящие закладки приложений: динамический ввод пользовательского интерфейса потерян


person ledogbert    schedule 27.06.2017    source источник
comment
Вам нужно использовать функцию onRestore(). Взгляните на эту статью: shiny.rstudio.com/articles/advanced-bookmarking.html   -  person Bárbara Borges    schedule 28.06.2017
comment
Привет, Барбара, спасибо за ссылку. Я думаю, что вижу, в чем проблема - даже если в URL-адресе addElement имеет значение больше 1, он запускается только один раз. Однако я не уверен, как мне включить функцию onRestore() в свой код. Что именно я должен вызывать - запускать часть кода ObservationElement? Или просто парсинг в чистом HTML? Спасибо, я немного растерялся.   -  person ledogbert    schedule 28.06.2017


Ответы (2)


Вот минимальный пример, адаптированный из вашего приложения. Нет волшебного способа получить динамический контент на странице после закладки (если только вы не используете renderUI простым способом - то есть без какого-либо состояния, не связанного с вводом). Итак, в вашем случае вам просто нужно снова повторить работу, которую вы выполняете в приложении, внутри обратного вызова onRestore(). Лучший способ сделать это - реорганизовать код, чтобы появилась функция, которую можно было бы вызвать для добавления элемента как в обычном приложении, так и в обратном вызове onRestore().

library(shiny)

ui <- function(request) {
  fluidPage(
    bookmarkButton(),
    sidebarLayout(
      actionButton("add", "Add Element"),
      mainPanel(id = "content")
    )
  )
}

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

  addItem <- function(id, textId, numberId, textVal = "", numberVal = NULL) {
    insertUI(
      selector = "#content",
      where = "beforeEnd",
      ui = tagList(
        wellPanel(style = "display:-webkit-inline-box;width:100%",
          id = id,
          column(3, textInput(textId, "Element name", textVal)),
          column(3, numericInput(numberId, "Element value", numberVal))
        )
      )
    )
  }

  observeEvent(input$add, {
    id <- paste0("var", input$add)
    textId <- paste0(id, "text")
    numberId <- paste0(id, "number")
    addItem(id, textId, numberId)
  }, ignoreInit = TRUE)

  onRestore(function(state) {
    for (i in seq_len(input$add))  {
      id <- paste0("var", i)
      textId <- paste0(id, "text")
      numberId <- paste0(id, "number")
      addItem(id, textId, numberId, input[[textId]], input[[numberId]])
    }
  })
}

enableBookmarking("url")
shinyApp(ui, server)
person Bárbara Borges    schedule 28.06.2017
comment
Я боялся, что это будет так - у меня довольно много этого динамически генерируемого контента, поэтому в целом он будет немного беспорядочным. Большое спасибо за ваше время, ценим вклад! - person ledogbert; 28.06.2017
comment
Также я понял, что могу использовать функцию внутри обработчика ObserverExpr. Поэтому я создал функцию, которую я вызываю ib как в ObservationEvent, так и на этапе onRestore. Пока все работает нормально. - person ledogbert; 29.06.2017
comment
@ledogbert Хотите знать, можете ли вы опубликовать код, который вы использовали для создания функции, которая будет обрабатываться наблюдателями и onRestore. Я пытаюсь сделать что-то подобное, но восстановление не удается без каких-либо полезных сообщений об ошибке ... - person John Gagnon; 09.07.2018

Это новый ответ, расширяющий ответ Барбары. Кажется, что нам нужно ссылаться на переменную состояния, чтобы функция onRestore правильно работала с вводом пользователя. Поэтому я изменил ее код, добавив state$input$var, который, похоже, работает. Надеюсь, это поможет.

library(shiny)

ui <- function(request) {
    fluidPage(
        bookmarkButton(),
        sidebarLayout(
            actionButton("add", "Add Element"),
            mainPanel(id = "content")
        )
    )
}

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

    addItem <- function(id, textId, numberId, textVal = "", numberVal = NULL) {
        insertUI(
            selector = "#content",
            where = "beforeEnd",
            ui = tagList(
                wellPanel(style = "display:-webkit-inline-box;width:100%",
                          id = id,
                          column(3, textInput(textId, "Element name", textVal)),
                          column(3, numericInput(numberId, "Element value", numberVal))
                )
            )
        )
    }

    observeEvent(input$add, {
        id <- paste0("var", input$add)
        textId <- paste0(id, "text")
        numberId <- paste0(id, "number")
        addItem(id, textId, numberId)
    }, ignoreInit = TRUE)

    onRestore(function(state) {
        for (i in seq_len(input$add))  {
            id <- paste0("var", i)
            textId <- paste0(id, "text")
            numberId <- paste0(id, "number")
            addItem(id, textId, numberId, state$input[[textId]], state$input[[numberId]])
        }
    })

}

enableBookmarking("server")
shinyApp(ui, server) 
person Daniel Avancini    schedule 03.09.2018