嵌入闪亮应用程序中的数据表中的条件垂直滚动

时间:2015-12-31 15:08:57

标签: javascript r datatables shiny dt

实际问题

我通过DataTable创建了DT::datatable接口/通过DT::renderDataTable呈现。

  1. 如何有条件启用垂直滚动,方法是根据shiny::checkboxInput的值做出决定?

  2. 启用垂直滚动时,如何控制数据表的高度?

    我完全理解选项scrollYscrollCollapse的含义以及他们与DT::renderDataTable的其他选项或应用本身的互动(例如,垂直滚动“app windows”本身)。

  3. 实施例

    在以下示例中,我尝试使scrollY的选项DT::renderDataTable的值取决于复选框输入(input$action_enable_scrolling)以及定义高度的输入(以像素为单位) (input$scrolling_y_limit)。

    问题:

    生成的渲染表不会反应性地反映正在进行的选择。似乎一旦考虑了input$action_enable_scrollinginput$scrolling_y_limit的初始值,它们就不能再被动地改变了

    您将看到,通过更改默认值,数据表部分的行为方式不同:

    • DFLT_action_enable_scrolling <- TRUE
    • DFLT_scrolling_y_limit <- 400

    全局

    # Packages ----------------------------------------------------------------
    
    library(shiny)
    
    # Variables ----------------------------------------------------------------
    
    DFLT_action_enable_scrolling <- FALSE
    DFLT_scrolling_y_limit <- 800
    
    # Functions ---------------------------------------------------------------
    
    createRecord <- function(input, db) {
      db$data <- rbind(
        db$data,
        data.frame(
          task = input$task,
          time = input$time,
          time_unit = "hour",
          stringsAsFactors = FALSE
        )
      )
    }
    updateRecord <- function(input, db, selection) {
      db$data[selection,] <- data.frame(
        task = input$task,
        time = input$time,
        time_unit = "hour",
        stringsAsFactors = FALSE
      )
    }
    deleteRecord <- function(db, selection) {
      db$data <- db$data[-selection,]
    }
    niceNames <- function(x) {
      s <- strsplit(x, " |_|\\.", perl = TRUE)[[1]]
      paste(toupper(substring(s, 1,1)), substring(s, 2),
        sep = "", collapse = " ")
    }
    

    UI

    ui <- fluidPage(
      div(
        style = "display:inline-block",
        p(),
        actionButton("action_trigger", "Create")
      ),
      tabsetPanel(
        tabPanel(
          title = "Scrolling options",
            checkboxInput("action_enable_scrolling", "Enable Y-scrolling",
              value = DFLT_action_enable_scrolling),
            numericInput("scrolling_y_limit", "Height limit for Y-scrolling (in px)",
              value = DFLT_scrolling_y_limit)
        )
      ),
      hr(),
      uiOutput("ui_input"),
      hr(),
      h3("Database"),
      DT::dataTableOutput("dt")
    )
    

    服务器

    server <- function(input, output, session) {
      ## Initialize DB //
      db <- reactiveValues(data = data.frame(
        task = character(),
        time = numeric(),
        time_unit = character()
      )[-1,])
    
      ## UI control //
      ui_control <- reactiveValues(
        case = c("hide", "create", "update")[1],
        selection = NULL,
        refresh = TRUE
      )
      observeEvent(input$action_trigger, {
        ui_control$case <- "create"
      })
    
      ## Render UI //
      output$ui_input <- renderUI({
        case <- ui_control$case
        if (case == "hide")
          return()
    
        ## Case dependent input //
        if (case == "create") {
          task <- ifelse(is.null(tmp <- isolate(input$task)), "", tmp)
          time <- ifelse(is.null(tmp <- isolate(input$time)), "", tmp)
    
          buttons <- div(
            style = "display:inline-block",
            actionButton("action_create", "Create"),
            actionButton("action_cancel", "Cancel")
          )
          updateTextInput(session, "first")
        } else if (case == "update") {
          task <- db$data[ui_control$selection, "task"]
          time <- db$data[ui_control$selection, "time"]
          buttons <- div(
            style = "display:inline-block",
            actionButton("action_update", "Update"),
            actionButton("action_cancel", "Cancel"),
            p(),
            actionButton(
              "action_delete",
              "Delete",
              icon = icon("exclamation-triangle")
            )
          )
        } else {
          stop(sprintf("Invalid case: %s", case))
        }
    
        tagList(
          textInput("task", "Task", task),
          numericInput("time", "Time", time),
          buttons
        )
      })
    
      ## CRUD operations //
      observeEvent(input$action_create, {
        createRecord(input, db = db)
        ui_control$case <- "hide"
      })
      observeEvent(input$action_update, {
        updateRecord(input, db = db, selection = ui_control$selection)
        ui_control$refresh <- NULL
        ui_control$refresh <- TRUE
        # ui_control$case <- "hide"
      })
      observeEvent(input$action_delete, {
        deleteRecord(db = db, selection = ui_control$selection)
        tmp <- ui_control$selection[1] - 1
        if (tmp == 0) tmp <- NULL
        ui_control$selection <- tmp
        ui_control$refresh <- NULL
        ui_control$refresh <- TRUE
        # ui_control$case <- "hide"
      })
      observeEvent(input$action_cancel, {
        ui_control$case <- "hide"
      })
    
      ## Selection //
      observe({
        idx <- input$dt_rows_selected
        ui_control$selection <- idx
      })
      observe({
        idx <- ui_control$selection
        if (!is.null(idx)) {
          ui_control$case <- "update"
        } else {
          ui_control$case <- "hide"
        }
      })
    
      ## Render table: preparations //
      observeEvent(input$action_enable_scrolling, {
        ui_control$refresh <- NULL
        ui_control$refresh <- TRUE
      })
      observeEvent(input$scrolling_y_limit, {
        ui_control$refresh <- NULL
        ui_control$refresh <- TRUE
      })
      dt_options = reactive({
        scroll <- input$action_enable_scrolling
        list(
          dom = "ltipr",
          autoWidth = TRUE,
          scrollX = TRUE,
          scrollY = if (scroll) {
            sprintf("%spx", input$scrolling_y_limit * 1)
          },
          scrollCollapse = if (scroll) {
            TRUE
          },
          lengthMenu = list(
            c(3, 5, -1),
            c(3, 5, "All")
          ),
          iDisplayLength = 3
        )
      })
    
      # Render table: DT //
      output$dt <- DT::renderDataTable({
        if (!ui_control$refresh) {
          return()
        }
        ## Note:
        ## Not really necessary for this example use case as `db$data` already
        ## introduces a reactive dependency.
        ## However, that might not always be the case for data I/O when an
        ## actual database is involved. In this case, this part will most likely
        ## have to be informed about required re-rendering by an explicit reactive
        ## value that other parts update upon I/O operations
    
        tmp <- db$data
        names(tmp) <- sapply(names(tmp), niceNames)
        tmp
      }, selection = "single", options = dt_options())
    
      # DT proxy //
      proxy <- DT::dataTableProxy("dt")
    
      ## Keep/restory previous selection //
      observe({
        ui_control$refresh
        DT::selectRows(proxy, as.numeric(ui_control$selection))
      })
    
      ## Resets //
      observe({
        if (ui_control$case == "create") {
          updateTextInput(session, "task", value = sprintf("Test %s", Sys.time()))
          updateTextInput(session, "time", value = 1)
        }
      })
    }
    

    生成

    shinyApp(ui, server)
    

    Gist上的参考应用

    上面使用的部分也包含在我的reference app中,它包含了一些与数据功能相关的东西/学习内容,如果你感兴趣的话:

    shiny::runGist("https://gist.github.com/rappster/d48916fbf8e8d0456ae2")
    

0 个答案:

没有答案