【问题标题】:R Shiny update textInput fields' in DT on changing variable selectionR Shiny 在更改变量选择时更新 DT 中的 textInput 字段
【发布时间】:2020-12-13 12:42:04
【问题描述】:

我正在构建一个应用程序,用户在其中加载 .RData 数据集(可以从 here 下载该文件)并从列表中选择变量 (DT),将其移动到另一个列表(也是 @987654324 @),然后可用的因子水平显示在下方的第三个DT 中。第三个DT 也有一列动态生成的textInput 字段,这些字段与变量的可用因子水平的数量相匹配,用户可以在其中为现有因子水平添加新值。输入的值存储在reactiveValues 对象中。现在该对象只是打印在 R 控制台中。该应用程序如下所示:

library(shiny)
library(DT)
library(data.table)
library(shinyFiles)


ui <- fluidPage(
  
  shinyFilesButton(id = "recodeChooseSrcFile", label = "Choose data file", title = "Navigate and select a file", multiple = FALSE),
  
  fluidRow(
    column(width = 6,
           DTOutput(outputId = "recodeAllAvailableVars"),
    ),
    column(width = 1, align = "center",
           br(), br(), br(), br(), br(), br(), br(), br(), br(), br(),
           uiOutput(outputId = "recodeArrowSelVarsRight"),
           br(), br(),
           uiOutput(outputId = "recodeArrowSelVarsLeft"),
    ),
    column(width = 5,
           DTOutput(outputId = "recodeVarsSelection"),
    ),
    br(), br()
  ),
  
  br(), br(),
  DTOutput(outputId = "recodeScheme")
  
)


server <- function(input, output, session) {
  
  available.volumes <- getVolumes()()
  
  file.var.recode <- reactiveValues(loaded = NULL, var.levels = NULL)
  
  # Select file and extract the variables.
  shinyFileChoose(input, "recodeChooseSrcFile", roots = available.volumes, filetype = list(RData = "RData"))
  
  observeEvent(eventExpr = input$recodeChooseSrcFile, {
    
    if(length(parseFilePaths(available.volumes, input$recodeChooseSrcFile)$datapath) > 0) {
      
      file.var.recode$loaded <- get(load(parseFilePaths(available.volumes, input$recodeChooseSrcFile)$datapath))
      
      file.var.recode$var.levels <- Filter(Negate(is.null), lapply(X = file.var.recode$loaded, FUN = function(i) {
        if(is.null(attr(x = i, which = "levels"))) {
          NULL
        } else {
          attr(x = i, which = "levels")
        }
      }))
      
      file.var.recode$loaded <- data.table(Variables = names(file.var.recode$loaded),
      
      order_col = 1:ncol(file.var.recode$loaded))
    }
  }, ignoreInit = TRUE)
  
  
  observe({
    
    var.props.initial.available.vars <- data.table(Variables = as.character(), order_col = as.numeric())
    var.props.initial.selected.vars <- data.table(Variables = as.character(), order_col = as.numeric())
    
    recodeAllVars <- reactiveValues(recodeAvailVars = var.props.initial.available.vars, recodeSelectedVars = var.props.initial.selected.vars)
    
    # Observe if the file is loaded, and if yes, update the table of available variables.
    observe({
      if(!is.null(file.var.recode$loaded)) {
        recodeAllVars$recodeAvailVars <- file.var.recode$loaded
      }
    })
    
    # Render the arrow buttons for selecting the variables.
    output$recodeArrowSelVarsRight <- renderUI({
      if(is.null(file.var.recode$loaded)) {
        return(NULL)
      } else {
        actionButton(inputId = "recodeArrowSelVarsRight", label = NULL, icon("angle-right"), width = "50px")
      }
    })
    
    output$recodeArrowSelVarsLeft <- renderUI({
      if(is.null(file.var.recode$loaded)) {
        return(NULL)
      } else {
        actionButton(inputId = "recodeArrowSelVarsLeft", label = NULL, icon("angle-left"), width = "50px")
      }
    })
    
    # Render the data table with the available variables.
    output$recodeAllAvailableVars <- renderDT({

      if(is.null(file.var.recode$loaded)) {
        return(NULL)
      } else {
        setkeyv(x = recodeAllVars$recodeAvailVars, cols = "order_col")
      }

    },
    caption = "Available variables",
    rownames = FALSE,
    colnames = c("Names", "sortingcol"),
    options = list(
      ordering = FALSE,
      columnDefs = list(list(visible = FALSE, targets = 1))
    ))
    
    
    
    # Render the table with the selected variables.
    output$recodeVarsSelection <- renderDT({
      if(is.null(file.var.recode$loaded)) {
        return(NULL)
      } else {
        setkeyv(x = recodeAllVars$recodeSelectedVars, cols = "order_col")
      }
    },
    
    caption = "Available variables",
    rownames = FALSE,
    colnames = c("Names", "sortingcol"),
    options = list(
      ordering = FALSE,
      columnDefs = list(list(visible = FALSE, targets = 1))
      
    ))
    
    # Observe the events of moving variables from available to selected.
    observeEvent(input$recodeArrowSelVarsRight, {
      req(input$recodeAllAvailableVars_rows_selected)
      recodeAllVars$recodeSelectedVars <- rbind(isolate(recodeAllVars$recodeSelectedVars), recodeAllVars$recodeAvailVars[input$recodeAllAvailableVars_rows_selected, , drop = F])
      recodeAllVars$recodeSelectedVars <- recodeAllVars$recodeSelectedVars[complete.cases(recodeAllVars$recodeSelectedVars[ , "Variables"]), , drop = FALSE]
      recodeAllVars$recodeAvailVars <- isolate(recodeAllVars$recodeAvailVars[-input$recodeAllAvailableVars_rows_selected, , drop = F])
    })
    
    observeEvent(input$recodeArrowSelVarsLeft, {
      req(input$recodeVarsSelection_rows_selected)
      recodeAllVars$recodeAvailVars <- rbind(isolate(recodeAllVars$recodeAvailVars), recodeAllVars$recodeSelectedVars[input$recodeVarsSelection_rows_selected, , drop = F])
      recodeAllVars$recodeAvailVars <- recodeAllVars$recodeAvailVars[complete.cases(recodeAllVars$recodeAvailVars[ , "Variables"]), , drop = FALSE]
      recodeAllVars$recodeSelectedVars <- isolate(recodeAllVars$recodeSelectedVars[-input$recodeVarsSelection_rows_selected, , drop = F])
    })
    
    # Render the table with the text inputs.
    initial.recode.new.values <- reactiveValues(values = NULL)
    
    entered.new.values <- reactiveValues(values = NULL)
    
    new.recoding.values <- reactiveValues(values = NULL)
    
    shinyInput <- function(obj) {
      tmp <- unlist(lapply(X = seq_along(obj), FUN = function(i) {
        i <- paste0(textInput(inputId = paste0("numinp", i), label = NULL, value = NULL, width = "50px"))
      }))
      return(tmp)
    }
    
    shinyValue <- function(id, len) {
      unlist(lapply(seq_len(len), function(i) {
        input[[paste0(id, i)]]
      }))
    }
    
    # Observe the changes in user selection and update the reactive values from above.
    observe({
      
      initial.recode.new.values$values <- unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]]))
      
      entered.new.values$values <- data.table(
        V1 = initial.recode.new.values$values,
        V2 = initial.recode.new.values$values,
        V3 = rep(x = "->", times = length(initial.recode.new.values$values)),
        V4 = shinyInput(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]])))
      )
      
      new.recoding.values$values <- shinyValue(id = "numinp", len = length(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]]))))
      
    })
    
    # Render the table with available levels and empty input fields where the user can enter his desired new values.
    output$recodeScheme <- renderDT({
      
      if(!is.null(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]])))) {
        entered.new.values$values
      } else {
        return(NULL)
      }
      
    },
    rownames = FALSE,
    colnames = c("Available variable values", "Old", "->", "New"),
    class = "cell-border stripe;compact cell-border;",
    selection="none",
    escape = FALSE,
    options = list(
      pageLength = 1500,
      dom = 'BRrt',
      rowCallback = JS("function(r,d) {$(r).attr('height', '40px')}"),
      preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
      drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')))
    
    observe({
      print(new.recoding.values$values)
    })
    
  })
}

shinyApp(ui, server)

选择变量后一切正常,新输入的值会立即更新并在每次击键时显示在控制台中。但是,如果用户决定从选定变量的DT 中删除变量,则new.recoding.values$values 反应值立即变为NULL(如预期的那样),但是当另一个变量添加到选定变量的DT 时,前一个变量的旧值会立即恢复并且永远不会更新。另外,如果新变量的级别比第一个输入的多,那么最后一个可以更新,但不能更新之前的(尝试输入ASBG03,然后用ASBG04替换它以了解我的意思)。

我真的不明白为什么会这样。到目前为止,我尝试将new.recoding.values$values 明确设置为NULL

1.在shinyValue函数运行之前生成它的观察者。

2.在按下右箭头按钮的observeEvent中,即:

observeEvent(input$recodeArrowSelVarsLeft, {
  req(input$recodeVarsSelection_rows_selected)
  recodeAllVars$recodeAvailVars <- rbind(isolate(recodeAllVars$recodeAvailVars), 
  recodeAllVars$recodeSelectedVars[input$recodeVarsSelection_rows_selected, , drop = F])
  recodeAllVars$recodeAvailVars <- recodeAllVars$recodeAvailVars[complete.cases(recodeAllVars$recodeAvailVars[ , "Variables"]), , drop = FALSE]
  recodeAllVars$recodeSelectedVars <- isolate(recodeAllVars$recodeSelectedVars[-input$recodeVarsSelection_rows_selected, , drop = F])
  new.recoding.values$values <- NULL
})

更新:

3.按照 Tonio Liebrand 的建议,我尝试如下更新文本输入(在渲染最后一个 DT 后添加):

observe({
      if(nrow(entered.new.values$values) == 0) {
        lapply(seq_len(length(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]])))), function(i) {
          updateTextInput(session,
                          input[[paste0("numinp", i)]],
                          value = NULL,
                          label = NULL)
        })
      }
    })

这些都没有帮助。每次我删除最初选择的变量时,new.recoding.values$values 在控制台中打印为NULL,但随后添加另一个变量new.recoding.values$values 突然恢复首先输入的第一个值,就像它仍然“记住”第一个输入一样。

我不太了解这种行为,有人可以帮助克服这个问题,即真的更新变量变化吗?

【问题讨论】:

  • 您的最终目标是什么?可能有更好的方法来做到这一点。您想让用户更改原始表格中的因素吗?
  • 简短版本是您还必须使用 updateTextInputsession 中删除值。否则,您只需将它们从用户界面中删除。挖掘你的代码有点麻烦,因为它并不是最小的。但是您会确定删除了哪一列并为相应的文本输入应用updateTextInput
  • @Tonio 是的,对于冗长的代码感到抱歉,我试图尽可能具体和详细。我按照您的建议编辑了这篇文章,除了我认为必须考虑的不是已删除的列(这实际上失败了),而是用户输入的值。不过,它不会更新它们。你能帮忙吗,我真的很痛苦。

标签: r shiny reactive dt textinput


【解决方案1】:

由于textFields 是在datatable 中创建的,因此您需要在再次使用该表之前解除绑定(updateTextInput 不起作用)。使用来自this 答案的代码,我添加了带有 unbind 函数的 JS 脚本,并在左箭头的观察者中调用了该函数。然后你得到一个工作应用程序:

library(shiny)
library(DT)
library(data.table)
library(shinyFiles)


ui <- fluidPage(
  tags$head(tags$script(
    HTML(
      "Shiny.addCustomMessageHandler('unbindDT', function(id) {
        var $table = $('#'+id).find('table');
        if($table.length > 0){
          Shiny.unbindAll($table.DataTable().table().node());
        }
      })")
  )),
  
  shinyFilesButton(id = "recodeChooseSrcFile", label = "Choose data file", title = "Navigate and select a file", multiple = FALSE),
  
  fluidRow(
    column(width = 6,
           DTOutput(outputId = "recodeAllAvailableVars"),
    ),
    column(width = 1, align = "center",
           br(), br(), br(), br(), br(), br(), br(), br(), br(), br(),
           uiOutput(outputId = "recodeArrowSelVarsRight"),
           br(), br(),
           uiOutput(outputId = "recodeArrowSelVarsLeft"),
    ),
    column(width = 5,
           DTOutput(outputId = "recodeVarsSelection"),
    ),
    br(), br()
  ),
  
  br(), br(),
  DTOutput(outputId = "recodeScheme")
  
)


server <- function(input, output, session) {
  
  available.volumes <- getVolumes()()
  
  file.var.recode <- reactiveValues(loaded = NULL, var.levels = NULL)
  
  # Select file and extract the variables.
  shinyFileChoose(input, "recodeChooseSrcFile", roots = available.volumes, filetype = list(RData = "RData"))
  
  observeEvent(eventExpr = input$recodeChooseSrcFile, {
    
    if(length(parseFilePaths(available.volumes, input$recodeChooseSrcFile)$datapath) > 0) {
      
      file.var.recode$loaded <- get(load(parseFilePaths(available.volumes, input$recodeChooseSrcFile)$datapath))
      
      file.var.recode$var.levels <- Filter(Negate(is.null), lapply(X = file.var.recode$loaded, FUN = function(i) {
        if(is.null(attr(x = i, which = "levels"))) {
          NULL
        } else {
          attr(x = i, which = "levels")
        }
      }))
      
      file.var.recode$loaded <- data.table(Variables = names(file.var.recode$loaded),
                                           
                                           order_col = 1:ncol(file.var.recode$loaded))
    }
  }, ignoreInit = TRUE)
  
  
  observe({
    
    var.props.initial.available.vars <- data.table(Variables = as.character(), order_col = as.numeric())
    var.props.initial.selected.vars <- data.table(Variables = as.character(), order_col = as.numeric())
    
    recodeAllVars <- reactiveValues(recodeAvailVars = var.props.initial.available.vars, recodeSelectedVars = var.props.initial.selected.vars)
    
    # Observe if the file is loaded, and if yes, update the table of available variables.
    observe({
      if(!is.null(file.var.recode$loaded)) {
        recodeAllVars$recodeAvailVars <- file.var.recode$loaded
      }
    })
    
    # Render the arrow buttons for selecting the variables.
    output$recodeArrowSelVarsRight <- renderUI({
      if(is.null(file.var.recode$loaded)) {
        return(NULL)
      } else {
        actionButton(inputId = "recodeArrowSelVarsRight", label = NULL, icon("angle-right"), width = "50px")
      }
    })
    
    output$recodeArrowSelVarsLeft <- renderUI({
      if(is.null(file.var.recode$loaded)) {
        return(NULL)
      } else {
        actionButton(inputId = "recodeArrowSelVarsLeft", label = NULL, icon("angle-left"), width = "50px")
      }
    })
    
    # Render the data table with the available variables.
    output$recodeAllAvailableVars <- renderDT({
      
      if(is.null(file.var.recode$loaded)) {
        return(NULL)
      } else {
        setkeyv(x = recodeAllVars$recodeAvailVars, cols = "order_col")
      }
      
    },
    caption = "Available variables",
    rownames = FALSE,
    colnames = c("Names", "sortingcol"),
    options = list(
      ordering = FALSE,
      columnDefs = list(list(visible = FALSE, targets = 1))
    ))
    
    
    
    # Render the table with the selected variables.
    output$recodeVarsSelection <- renderDT({
      if(is.null(file.var.recode$loaded)) {
        return(NULL)
      } else {
        setkeyv(x = recodeAllVars$recodeSelectedVars, cols = "order_col")
      }
    },
    
    caption = "Available variables",
    rownames = FALSE,
    colnames = c("Names", "sortingcol"),
    options = list(
      ordering = FALSE,
      columnDefs = list(list(visible = FALSE, targets = 1))
      
    ))
    
    # Observe the events of moving variables from available to selected.
    observeEvent(input$recodeArrowSelVarsRight, {
      req(input$recodeAllAvailableVars_rows_selected)
      recodeAllVars$recodeSelectedVars <- rbind(isolate(recodeAllVars$recodeSelectedVars), recodeAllVars$recodeAvailVars[input$recodeAllAvailableVars_rows_selected, , drop = F])
      recodeAllVars$recodeSelectedVars <- recodeAllVars$recodeSelectedVars[complete.cases(recodeAllVars$recodeSelectedVars[ , "Variables"]), , drop = FALSE]
      recodeAllVars$recodeAvailVars <- isolate(recodeAllVars$recodeAvailVars[-input$recodeAllAvailableVars_rows_selected, , drop = F])
    })
    
    observeEvent(input$recodeArrowSelVarsLeft, {
      req(input$recodeVarsSelection_rows_selected)
      recodeAllVars$recodeAvailVars <- rbind(isolate(recodeAllVars$recodeAvailVars), recodeAllVars$recodeSelectedVars[input$recodeVarsSelection_rows_selected, , drop = F])
      recodeAllVars$recodeAvailVars <- recodeAllVars$recodeAvailVars[complete.cases(recodeAllVars$recodeAvailVars[ , "Variables"]), , drop = FALSE]
      recodeAllVars$recodeSelectedVars <- isolate(recodeAllVars$recodeSelectedVars[-input$recodeVarsSelection_rows_selected, , drop = F])
      session$sendCustomMessage("unbindDT", "recodeScheme")
    })
    
    # Render the table with the text inputs.
    initial.recode.new.values <- reactiveValues(values = NULL)
    
    entered.new.values <- reactiveValues(values = NULL)
    
    new.recoding.values <- reactiveValues(values = NULL)
    
    shinyInput <- function(obj) {
      tmp <- unlist(lapply(X = seq_along(obj), FUN = function(i) {
        i <- paste0(textInput(inputId = paste0("numinp", i), label = NULL, value = NULL, width = "50px"))
      }))
      return(tmp)
    }
    
    shinyValue <- function(id, len) {
      unlist(lapply(seq_len(len), function(i) {
        input[[paste0(id, i)]]
      }))
    }
    
    # Observe the changes in user selection and update the reactive values from above.
    observe({
      
      initial.recode.new.values$values <- unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]]))
      
      entered.new.values$values <- data.table(
        V1 = initial.recode.new.values$values,
        V2 = initial.recode.new.values$values,
        V3 = rep(x = "->", times = length(initial.recode.new.values$values)),
        V4 = shinyInput(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]])))
      )
      
      new.recoding.values$values <- shinyValue(id = "numinp", len = length(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]]))))
      
    })
    
    # Render the table with available levels and empty input fields where the user can enter his desired new values.
    output$recodeScheme <- renderDT({
      
      if(!is.null(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]])))) {
        entered.new.values$values
      } else {
        return(NULL)
      }
      
    },
    rownames = FALSE,
    colnames = c("Available variable values", "Old", "->", "New"),
    class = "cell-border stripe;compact cell-border;",
    selection="none",
    escape = FALSE,
    options = list(
      pageLength = 1500,
      dom = 'BRrt',
      rowCallback = JS("function(r,d) {$(r).attr('height', '40px')}"),
      preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
      drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')))
    
    observe({
      print(new.recoding.values$values)
    })
    
  })
}

shinyApp(ui, server)

但是,我建议您阅读有关反应性的更多信息,例如here。你使用了很多观察者,并且你嵌套了它们。我不建议这样做,因为这会导致奇怪的行为。另外,请尝试使用更多的reactive/reactiveExpression,因为observe/observeEvent 会使您的应用程序变慢。在找到正确的解决方案之前,我尝试将您的代码取消嵌套,但它仍然有效!这表明您的应用具有您实际上不需要的复杂性:

library(shiny)
library(DT)
library(data.table)
library(shinyFiles)

# additional functions
shinyInput <- function(obj) {
    tmp <- unlist(lapply(X = seq_along(obj), FUN = function(i) {
        i <- paste0(textInput(inputId = paste0("numinp", i), label = NULL, value = NULL, width = "50px"))
    }))
    return(tmp)
}

shinyValue <- function(id, len, input) {
    unlist(lapply(seq_len(len), function(i) {
        input[[paste0(id, i)]]
    }))
}


ui <- fluidPage(
    tags$head(tags$script(
        HTML(
            "Shiny.addCustomMessageHandler('unbindDT', function(id) {
        var $table = $('#'+id).find('table');
        if($table.length > 0){
          Shiny.unbindAll($table.DataTable().table().node());
        }
      })")
    )),
    shinyFilesButton(id = "recodeChooseSrcFile", label = "Choose data file", title = "Navigate and select a file", multiple = FALSE),
    
    fluidRow(
        column(width = 6,
               DTOutput(outputId = "recodeAllAvailableVars"),
        ),
        column(width = 1, align = "center",
               br(), br(), br(), br(), br(), br(), br(), br(), br(), br(),
               uiOutput(outputId = "recodeArrowSelVarsRight"),
               br(), br(),
               uiOutput(outputId = "recodeArrowSelVarsLeft"),
        ),
        column(width = 5,
               DTOutput(outputId = "recodeVarsSelection"),
        ),
        br(), br()
    ),
    
    br(), br(),
    DTOutput(outputId = "recodeScheme")
    
)


server <- function(input, output, session) {
    
    available.volumes <- getVolumes()()
    
    file.var.recode <- reactiveValues(loaded = NULL, var.levels = NULL)
    
    # define variables
    # Render the table with the text inputs.
    initial.recode.new.values <- reactiveValues(values = NULL)
    
    entered.new.values <- reactiveValues(values = NULL)
    
    new.recoding.values <- reactiveValues(values = NULL)
    
    # Select file and extract the variables.
    shinyFileChoose(input, "recodeChooseSrcFile", roots = available.volumes, filetype = list(RData = "RData"))
    
    observeEvent(eventExpr = input$recodeChooseSrcFile, {
        
        if(length(parseFilePaths(available.volumes, input$recodeChooseSrcFile)$datapath) > 0) {
            
            file.var.recode$loaded <- get(load(parseFilePaths(available.volumes, input$recodeChooseSrcFile)$datapath))
            
            file.var.recode$var.levels <- Filter(Negate(is.null), lapply(X = file.var.recode$loaded, FUN = function(i) {
                if(is.null(attr(x = i, which = "levels"))) {
                    NULL
                } else {
                    attr(x = i, which = "levels")
                }
            }))
            
            file.var.recode$loaded <- data.table(Variables = names(file.var.recode$loaded),
                                                 
                                                 order_col = 1:ncol(file.var.recode$loaded))
        }
    }, ignoreInit = TRUE)
    
    recodeAllVars <- reactiveValues(recodeAvailVars = data.table(Variables = as.character(), order_col = as.numeric()),
                                    recodeSelectedVars = data.table(Variables = as.character(), order_col = as.numeric()))
    
    
    # Observe if the file is loaded, and if yes, update the table of available variables.
    observe({
        if(!is.null(file.var.recode$loaded)) {
            recodeAllVars$recodeAvailVars <- file.var.recode$loaded
        }
    })
    
    # Render the arrow buttons for selecting the variables.
    output$recodeArrowSelVarsRight <- renderUI({
        if(is.null(file.var.recode$loaded)) {
            return(NULL)
        } else {
            actionButton(inputId = "recodeArrowSelVarsRight", label = NULL, icon("angle-right"), width = "50px")
        }
    })
    
    output$recodeArrowSelVarsLeft <- renderUI({
        if(is.null(file.var.recode$loaded)) {
            return(NULL)
        } else {
            actionButton(inputId = "recodeArrowSelVarsLeft", label = NULL, icon("angle-left"), width = "50px")
        }
    })
    
    # Render the data table with the available variables.
    output$recodeAllAvailableVars <- renderDT({
        
        if(is.null(file.var.recode$loaded)) {
            return(NULL)
        } else {
            setkeyv(x = recodeAllVars$recodeAvailVars, cols = "order_col")
        }
        
    },
    caption = "Available variables",
    rownames = FALSE,
    colnames = c("Names", "sortingcol"),
    options = list(
        ordering = FALSE,
        columnDefs = list(list(visible = FALSE, targets = 1))
    ))
    
    # Render the table with the selected variables.
    output$recodeVarsSelection <- renderDT({
        if(is.null(file.var.recode$loaded)) {
            return(NULL)
        } else {
            setkeyv(x = recodeAllVars$recodeSelectedVars, cols = "order_col")
        }
    },
    
    caption = "Available variables",
    rownames = FALSE,
    colnames = c("Names", "sortingcol"),
    options = list(
        ordering = FALSE,
        columnDefs = list(list(visible = FALSE, targets = 1))
        
    ))
    
    # Observe the events of moving variables from available to selected.
    observeEvent(input$recodeArrowSelVarsRight, {
        req(input$recodeAllAvailableVars_rows_selected)
        recodeAllVars$recodeSelectedVars <- rbind(isolate(recodeAllVars$recodeSelectedVars), recodeAllVars$recodeAvailVars[input$recodeAllAvailableVars_rows_selected, , drop = F])
        recodeAllVars$recodeSelectedVars <- recodeAllVars$recodeSelectedVars[complete.cases(recodeAllVars$recodeSelectedVars[ , "Variables"]), , drop = FALSE]
        recodeAllVars$recodeAvailVars <- isolate(recodeAllVars$recodeAvailVars[-input$recodeAllAvailableVars_rows_selected, , drop = F])
    })
    
    observeEvent(input$recodeArrowSelVarsLeft, {
        req(input$recodeVarsSelection_rows_selected)
        recodeAllVars$recodeAvailVars <- rbind(isolate(recodeAllVars$recodeAvailVars), recodeAllVars$recodeSelectedVars[input$recodeVarsSelection_rows_selected, , drop = F])
        recodeAllVars$recodeAvailVars <- recodeAllVars$recodeAvailVars[complete.cases(recodeAllVars$recodeAvailVars[ , "Variables"]), , drop = FALSE]
        recodeAllVars$recodeSelectedVars <- isolate(recodeAllVars$recodeSelectedVars[-input$recodeVarsSelection_rows_selected, , drop = F])
        
        session$sendCustomMessage("unbindDT", "recodeScheme")
    })
    
    # Observe the changes in user selection and update the reactive values from above.
    observe({
        
        initial.recode.new.values$values <- unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]]))
        
        entered.new.values$values <- data.table(
            V1 = initial.recode.new.values$values,
            V2 = initial.recode.new.values$values,
            V3 = rep(x = "->", times = length(initial.recode.new.values$values)),
            V4 = shinyInput(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]])))
        )
        
        new.recoding.values$values <- shinyValue(id = "numinp",
                                                 len = length(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]]))),
                                                 input = input)
        
    })
    
    # Render the table with available levels and empty input fields where the user can enter his desired new values.
    output$recodeScheme <- renderDT({
        
        if(!is.null(unlist(unique(file.var.recode$var.levels[recodeAllVars$recodeSelectedVars[ , Variables]])))) {
            entered.new.values$values
        } else {
            return(NULL)
        }
        
    },
    rownames = FALSE,
    colnames = c("Available variable values", "Old", "->", "New"),
    class = "cell-border stripe;compact cell-border;",
    selection="none",
    escape = FALSE,
    options = list(
        pageLength = 1500,
        dom = 'BRrt',
        rowCallback = JS("function(r,d) {$(r).attr('height', '40px')}"),
        preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
        drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')))
    
    observe({
        print(new.recoding.values$values)
    })
    
    
    
    # end of server
}



shinyApp(ui, server)

还有一些改进的空间,例如您可以尝试使用reactive 而不是observe 用于以下sn-p:

    # Observe if the file is loaded, and if yes, update the table of available variables.
    observe({
        if(!is.null(file.var.recode$loaded)) {
            recodeAllVars$recodeAvailVars <- file.var.recode$loaded
        }
    })

【讨论】:

  • 非常感谢您花时间和精力来解决它,还优化了我的代码,我真的很感激。你是对的,我应该在不嵌套的情况下使用更少的观察者,对 Shiny 来说还是新手,还有很多东西要学。也感谢您提供的来源。
  • 我还有一个关于您在开头提供的javascript代码以及通过session$sendCustomMessage("unbindDT", "recodeScheme")调用它来解除绑定表的问题。我不知道javascript,但我是否正确理解开头的脚本是通用的,可用于通过指向DT(在此recodeScheme)来取消绑定应用程序中DTs 中的其他元素案子)。它会取消绑定此表中所有动态生成的输入吗?假设我不仅输入了文本,还在每一行添加了selectInput
  • 我也不擅长 JS,但是是的,这个函数是通用的,也可以用于其他表。对于第二部分:我认为它应该有效,但我不完全确定。正如我所说,我使用了@Stéphane Laurent 链接答案中的解决方案,他是闪亮 + JS 方面的专家
  • 这很有帮助,非常感谢!还有很多东西要学。
猜你喜欢
  • 2019-07-17
  • 2018-03-04
  • 2023-03-22
  • 2019-08-15
  • 2017-06-11
  • 1970-01-01
  • 1970-01-01
  • 2021-09-01
  • 2020-04-14
相关资源
最近更新 更多