R Shiny 在更改变量选择时更新 DT 中的 textInput 字段

R Shiny update textInput fields' in DT on changing variable selection

我正在构建一个应用程序,用户可以在其中加载 .RData 数据集(可以从 here 下载文件)并从列表 (DT) 中选择变量,移动它到另一个列表(也是 DT),然后可用的因子水平显示在下面的第三个 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 in:

1.The观察者生成的地方,在shinyValue函数之前是运行.

2.In 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.Following Tonio Lieb运行d 的建议,我尝试按如下方式更新文本输入(在渲染最后一个 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)
        })
      }
    })

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

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

因为 textFields 是在 datatable 中创建的,您需要在再次使用 table 之前解除绑定(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

    # 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
        }
    })