skip to Main Content

In running the below simplified code, the user can add or remove table rows by right-clicking on a row which, through the rhandsontable package context menu, generates a pop-up of action choices. In the code you can see how I used the onRender(...) function and JavaScript to deactivate row deletion when there is only 1 row in the table. This works.

However, I would also like to either deactivate, or remove (whichever is simpler), the "Insert row above" selection when the user accesses the context menu from the first row of the table. When the user accesses the context menu from any table row other than the first then "Insert row above" should be functional. Basically, I never want a top row that is blank. Any ideas for how to do this?

I went through the handsontable listing of hooks and could not find any equivalent of "beforeAddRow" or the like.

library(shiny)
library(rhandsontable)
library(htmlwidgets)

ui <- fluidPage(
  br(),
  rHandsontableOutput("simple_table")
)

server <- function(input, output) {
  output$simple_table <- renderRHandsontable({
    rhandsontable(data.frame(Value = numeric(1)), contextMenu = TRUE, rowHeaders = TRUE) %>%
      hot_cols(colWidths = 100, type = "numeric") %>%
      # Below prevents row deletion when there is only 1 row in table
      onRender(
        c(
          "function(el, x) {",
          "  var hot = this.hot;",
          "  Handsontable.hooks.add('beforeRemoveRow', function(index, amount){",
          "    var nrows = hot.countRows();",
          "    if(nrows === 1) {",
          "      return false;",
          "    }",
          "  }, hot);",
          "}"
        )
      )
  })
}

shinyApp(ui = ui, server = server)

2

Answers


  1. Chosen as BEST ANSWER

    An alternate approach, using only JavScript, simply ignores any attempt by the user to insert a row above the first row of the table. Here is the key modified code from the server section:

    rhandsontable(data.frame(Value = numeric(1)), contextMenu = TRUE, rowHeaders = TRUE) %>%
      hot_cols(colWidths = 100, type = "numeric") %>%
      onRender(
        c(
          "function(el, x) {",
          "  var hot = this.hot;",
          "  Handsontable.hooks.add('beforeCreateRow', function(index, amount) {",
          "    if (index === 0) {",
          "      return false;",  
          "    }",
          "  });",
          "}"
        )
      )
    

  2. I think that the best option is to remove the not-needed items from the context menu because otherwise it may be confusing for the user why nothing happens when they click on some items. Here is a solution which does not need custom Javascript. I treat here the situation that you would like to have "Insert row above" and "Remove row" not to be visible if the table has only one row.

    The idea is that inside the renderRHandsontable we also have an assignment rhot$x$contextMenu <- ContextMenuItems(), where ContextMenuItems is a reactive containing the menu items which we update inside an observeEvent on the table. Inside this observeEvent we distinguish between the nrow() == 1 and nrow() > 1 case and set the items respectively.

    enter image description here

    library(shiny)
    library(rhandsontable)
    
    ui <- fluidPage(
      br(),
      rHandsontableOutput("simple_table")
    )
    
    server <- function(input, output) {
      
      ContextMenuItems <- reactiveVal(list(items = c("row_below",
                                                     "---------", "undo", "redo", 
                                                     "---------", "alignment")))
      
      Data <- reactiveVal(data.frame(Value = numeric(1)))
      
      observeEvent(input$simple_table, {
        Data(hot_to_r(input$simple_table))
        nrows_table <- nrow(Data())
        newIt <- ContextMenuItems()
        if (nrows_table == 1) {
          newIt$items <- newIt$items[!(newIt$items %in% c("row_above", "remove_row"))]
        } else if (!(any(c("row_above", "remove_row") %in% newIt$items))) {
          newIt$items <- newIt$items |> append("row_above", 0) |> append("remove_row", 2)
        }
        ContextMenuItems(newIt)
      })
                                      
      output$simple_table <- renderRHandsontable({
        rhot <- rhandsontable(Data(), contextMenu = TRUE, rowHeaders = TRUE) %>%
          hot_cols(colWidths = 100, type = "numeric")
        rhot$x$contextMenu <- ContextMenuItems()
        rhot
      })
    }
    
    shinyApp(ui = ui, server = server)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search