наблюдатели стреляют по визуализации динамического интерфейса - PullRequest
0 голосов
/ 02 мая 2018

Проблема, с которой я сталкиваюсь, заключается в том, что observers, связанный с динамически отображаемыми элементами, похоже, срабатывает на render, хотя я не хочу, чтобы это было так.

Причина, по которой это проблема, заключается в том, что цветовые кнопки, которые я делаю, связаны с графиком, на визуализацию которого уходит несколько секунд (plotly виджет)

Я добавил ignoreInit = T observers, которые были созданы, но они по-прежнему запускаются при рендеринге, в отличие от обычных наблюдателей, связанных со сборкой кнопок непосредственно в UI

Как мне остановить наблюдателей, связанных с динамически отображаемым colourInput, от запуска при визуализации элемента?

В приложении-пустышке ниже следующая серия событий воссоздана в упрощенном виде:
Модель выплевывает число (моделируется кнопкой теста в демонстрационном приложении) Основываясь на этом числе, сделан ряд кнопок colourInput Для каждого сделано одинаковое количество observeEvent.

Не в фиктивном приложении: когда пользователь выбирает изменение цвета, соответствующая группа на графиках перекрашивается соответствующим образом

Тестовое приложение содержит рабочий статический colourInput и динамическую часть, которая демонстрирует сценарий проблемы.

Тестовое приложение:

library(shiny)
 library("colourpicker")

THECOLORS <- c('#383838', '#5b195b','#1A237E', '#000080', '#224D17', '#cccc00', '#b37400',  '#990000', 
               '#505050',  '#a02ca0',  '#000099', '#2645e0', '#099441', '#e5e500', '#cc8400', '#cc0000', 
               '#737373', '#e53fe5', '#0000FF', '#4479e1',  '#60A830', '#ffff00','#e69500', '#ff0000', 
               '#b2b2b2', '#eb6ceb', '#6666ff', '#d0a3ff', '#9FDA40',  '#ffff7f', '#ffa500', '#ff4c4c')
ui <- fluidPage(      
      h1("WELCOME TO THE TEST APP", style = 'text-align: center; font-weight:bold' ),
    br(), 
    h3("STATIC PART: doesn't fire on startup, great!",  style = 'font-weight:bold'),
    div(colourpicker::colourInput(inputId = 'StaticColor', label = NULL, palette = "limited", allowedCols = THECOLORS, value = THECOLORS[14], showColour = "background", returnName = TRUE), 
    style = " height: 30px; width: 30px; border-radius: 6px;  border-width: 2px; text-align:center; padding: 0px; display:block; margin-bottom: 10px"),
    br(),
    h3("Dynamic part: fires on render, NOT great!",  style = 'font-weight:bold'),
    actionButton(inputId = 'Tester', label = 'Click me'),
    br(),
    uiOutput('colorbutton')
  )

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

 values <- reactiveValues()
 values$mycolors <- THECOLORS

 observeEvent(input$Tester, { values$NrofButtons <- sample(1:10, 1) })

 observeEvent(values$NrofButtons, { 
  COLElement <-    function(idx){sprintf("COL_button-%s-%d",values$NrofButtons,idx)}

  output$colorbutton <- renderUI({
    lapply(1:values$NrofButtons, function(x) { 
      div(colourpicker::colourInput(inputId = COLElement(x), label = NULL, palette = "limited", allowedCols = values$mycolors, value = values$mycolors[x], showColour = "background", returnName = TRUE), 
      style = " height: 30px; width: 30px; border-radius: 6px;  border-width: 2px; text-align:center; padding: 0px; display:block; margin-bottom: 10px")  })
  })

  lapply(1:values$NrofButtons, function(x) { observeEvent(input[[COLElement(x)]], { print(input[[COLElement(x)]] )}, ignoreInit = T)  }) # make observer for each button

 })

 observeEvent(input[['StaticColor']], { print(input[['StaticColor']] )}, ignoreInit = T)

}

shinyApp(ui,server)

1 Ответ

0 голосов
/ 02 мая 2018

Рендеринг всегда должен быть сам по себе и быть управляемым данными, а не событиями - поэтому я сделал, чтобы рендеринг требовал, чтобы количество цветов было определено перед рендерингом. Конечно, количество цветов не определяется, пока не будет нажата кнопка observeEvent.

В целом все еще существует проблема, заключающаяся в том, что при каждом нажатии кнопки все больше наблюдателей создаются для одного и того же идентификатора, работая над способом их автоматического уничтожения при последующем нажатии кнопки тестера.

Ключевое добавление было ignoreInit = TRUE в вашем observeEvent(input$Tester, {...}) наблюдателе.

library(shiny)
library("colourpicker")

THECOLORS <- c('#383838', '#5b195b','#1A237E', '#000080', '#224D17', '#cccc00', '#b37400',  '#990000', 
               '#505050',  '#a02ca0',  '#000099', '#2645e0', '#099441', '#e5e500', '#cc8400', '#cc0000', 
               '#737373', '#e53fe5', '#0000FF', '#4479e1',  '#60A830', '#ffff00','#e69500', '#ff0000', 
               '#b2b2b2', '#eb6ceb', '#6666ff', '#d0a3ff', '#9FDA40',  '#ffff7f', '#ffa500', '#ff4c4c')
ui <- fluidPage(      
  h1("WELCOME TO THE TEST APP", style = 'text-align: center; font-weight:bold' ),
  br(), 
  h3("STATIC PART: doesn't fire on startup, great!",  style = 'font-weight:bold'),
  div(colourpicker::colourInput(inputId = 'StaticColor', label = NULL, palette = "limited", allowedCols = THECOLORS, value = THECOLORS[14], showColour = "background", returnName = TRUE), 
      style = " height: 30px; width: 30px; border-radius: 6px;  border-width: 2px; text-align:center; padding: 0px; display:block; margin-bottom: 10px"),
  br(),
  h3("Dynamic part: fires on render, NOT great!",  style = 'font-weight:bold'),
  actionButton(inputId = 'Tester', label = 'Click me'),
  br(),
  uiOutput('colorbutton')
)


COLElement <- function(idx) sprintf("COL_button-%d", idx)

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

  values <- reactiveValues(previous_max = 1)

  observeEvent(input$Tester, {
    values$NrofButtons <- sample(1:10, 1)

    # reset counters for all observers
    for (i in seq(values$NrofButtons)) {
      values[[sprintf("observer%d_renders", i)]] <- 0L
    }

    # only initialize incremental observers
    lapply(values$previous_max:values$NrofButtons, function(x) { 
      observeEvent(input[[COLElement(x)]], { 
        # only execute the second time, since the `ignoreInit` isn't obeyed
        if (values[[sprintf("observer%d_renders", x)]] > 0) {
          print(input[[COLElement(x)]] )
        } else {
          values[[sprintf("observer%d_renders", x)]] <- 1L
        }

      }, ignoreInit = TRUE)
    }) # make observer for each button

    # record the max
    values$previous_max <- max(values$previous_max, max(values$NrofButtons))
  }, ignoreInit = TRUE)


  output$colorbutton <- renderUI({

    req(length(values$NrofButtons) > 0)

    lapply(1:values$NrofButtons, function(x) { 
      div(colourpicker::colourInput(
        inputId     = COLElement(x)
        , label       = NULL
        , palette     = "limited"
        , allowedCols = THECOLORS
        , value       = THECOLORS[x]
        , showColour  = "background"
        , returnName  = TRUE
      )
      , style = " height: 30px; width: 30px; border-radius: 6px;  border-width: 2px; text-align:center; padding: 0px; display:block; margin-bottom: 10px"
      )
    })
  })

  observeEvent(input$StaticColor, { 
    print(input$StaticColor )
  }, ignoreInit = TRUE)

}

shinyApp(ui,server)
...