Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

datatable row focus after editing

Tags:

r

dt

shiny

I'm using the datatables package in a shiny app and its working well, but when i edit a cell/value the focus changes from the current row to the first row. The table is long so having to scroll back to the row being edited is a problem. Is there a way of selecting a certain row? then I can return to the row being edited after the table reloads.

Here's the code for the datatable render in shiny. Any suggestions please?

output$book_table <- DT::renderDT(RVTables$book %>% 
                                  filter(deal == as.numeric(input$deal_choice)),
                                  selection = 'single',
                                  editable = TRUE,
                                  rownames = FALSE,
                                  options = list(
                                  autoWidth = TRUE,
                                  ordering = FALSE,
                                  pageLength = 12,
                                  scrollX = TRUE,
                                  scrollY = TRUE,
                                  bLengthChange = FALSE,
                                  searching = FALSE
                                  )
)

edit:

I found a way of selecting a row, but I can't dynamically update it.

row_edit<-3
    output$book_table <- DT::renderDT(RVTables$book %>% 
                                      filter(deal == as.numeric(input$deal_choice)),
                                      selection = list(mode='single',selected=row_edit)
                                      editable = TRUE,
                                      rownames = FALSE,
                                      options = list(
                                      autoWidth = TRUE,
                                      ordering = FALSE,
                                      pageLength = 12,
                                      scrollX = TRUE,
                                      scrollY = TRUE,
                                      bLengthChange = FALSE,
                                      searching = FALSE
                                      )
    )

and using a global assignment in the edit event also hasn't worked:

row_edit<<- 22

EDIT
Here's a sample app to show what I'm trying to do. Edit row 42 for example an see how the next row highlighted is back on the first page, which is annoying if you want to edit a single value multiple times to see the effect.

library(shiny)
library(shinyjs)
library(tidyverse)
library(DT) #load after shiny

data_input <- data.frame(ID=seq(1,50),
                   weight=sample(50:65,50,replace = TRUE),
                   height=sample(150:225,50,replace = TRUE)
)

#shiny---
shinyApp(

  #ui----
  ui= fluidPage(
    fluidRow(
      br(),
      column(2,
             actionButton("increase_weight","increase weight"),
             uiOutput("show_row_selected"),
             uiOutput("last_row_selected")
      ),
      column(4,DT::DTOutput("data_table"))

    )
  ),


  server=function(input,output,session){

    #save data frame as a reactive value
    RV <- reactiveValues(
      data=data_input
    )

    #try to save last row selected----
    row_select <- reactive({

      #case when code below fails, use row 3 to illustrate code
      3

      #uncomment code below and run to see error
      # case_when(
      #   #valid row selected
      #   length(input$data_table_rows_selected)>0 ~ as.numeric(input$data_table_rows_selected),
      #   #after update, row object is empty to use last selected row
      #   #this is now recursive and app fails - "evaluation nested too deeply: infinite recursion...."
      #   length(input$data_table_rows_selected)==0 ~ row_select()
      # )

    })

    #render data frame for output
        output$data_table <-  DT::renderDT(RV$data,
                                       selection = list(mode="single",selected=row_select()),
                                       editable = TRUE,
                                       rownames = FALSE,
                                       options=list(
                                         autoWidth=TRUE,
                                         scrollX = TRUE,
                                         ordering=FALSE,
                                         pageLength=12,
                                         scrollY = TRUE,
                                         bLengthChange= FALSE,
                                         searching=FALSE
                                       )
    )

        #edit a single cell----
        observeEvent(input$data_table_cell_edit, {
          info <-  input$data_table_cell_edit
          edit_row <-  info$row
          edit_col <-  info$col+1    # column index offset by 1
          edit_value <-  info$value

          #find leg to be edited
          ID <- edit_row
          RV$data[ID,edit_col] <-as.numeric(edit_value)

        })


        #increase weight by one----
        observeEvent(input$increase_weight, {
          edit_row <- input$data_table_rows_selected

          RV$data[edit_row,"weight"] <- RV$data[edit_row,"weight"]+1
        })

        #show current row selected----
        output$show_row_selected <- renderText({
          paste0("row selected:  ",as.character(as.numeric(input$data_table_rows_selected)))
        })

        #show last row selected----
        output$last_row_selected <- renderText({
          paste0("row selected:  ",as.character(row_select()))
        })

  })
like image 514
Zeus Avatar asked Jul 18 '18 07:07

Zeus


1 Answers

With help from this question, here's some code which works.

library(shiny)
library(shinyjs)
library(tidyverse)
library(DT) #load after shiny

data_input <- data.frame(ID=seq(1,50),
                         weight=sample(50:65,50,replace = TRUE),
                         height=sample(150:225,50,replace = TRUE)
)

#shiny---
shinyApp(

  #ui----
  ui= fluidPage(
    fluidRow(
      br(),
      column(2,
             actionButton("increase_weight","increase weight"),
             uiOutput("show_row_selected")
      ),
      column(4,DT::DTOutput("data_table"))
    )
  ),


  server=function(input,output,session){

    #save data frame as a reactive value
    RV <- reactiveValues(
      data=data_input
    )
    previousSelection <- NULL
    previousPage <- NULL

    #render data table
    output$data_table <- DT::renderDataTable({
      DT::datatable(
        RV$data,
        editable = TRUE,
        selection = list(mode = "single", target = "row", selected = previousSelection),
        options = list(
          autoWidth=TRUE,
          scrollX = TRUE,
          pageLength = 10, 
          displayStart = previousPage))
    })


    #edit a single cell----
    observeEvent(input$data_table_cell_edit, {
      info <-  input$data_table_cell_edit
      edit_row <-  info$row
      edit_col <-  info$col   
      edit_value <-  info$value

      previousSelection <<- input$data_table_rows_selected
      previousPage <<- input$data_table_rows_current[1] - 1

      #find leg to be edited
      RV$data[edit_row,edit_col] <-as.numeric(edit_value)

    })


    #increase weight by one----
    observeEvent(input$increase_weight, {
      edit_row <- input$data_table_rows_selected

      previousSelection <<- input$data_table_rows_selected
      previousPage <<- input$data_table_rows_current[1] - 1

      RV$data[edit_row,"weight"] <- RV$data[edit_row,"weight"]+1
    })

    #show current row selected----
    output$show_row_selected <- renderText({
      paste0("row selected:  ",as.character(as.numeric(input$data_table_rows_selected)))
    })

  })
like image 193
Zeus Avatar answered Oct 06 '22 01:10

Zeus