Home > Software engineering >  Using shinyjqui::sortableTableOutput with a reactive dataframe
Using shinyjqui::sortableTableOutput with a reactive dataframe

Time:08-17

If you run the app below, it works fine at the beginning: you can reorder the rows of mtcars and the order appears in the verbatimTextOutput. But once you change the data to iris with the radio button, that does not work anymore.

library(shiny)
library(shinyjqui)

ui <- fluidPage(
  radioButtons("radio", "Data", choices = c("mtcars", "iris")),
  verbatimTextOutput("rows"),
  sortableTableOutput("tbl")
)

server <- function(input, output) {

  Dat <- reactive({
    if(input[["radio"]] == "mtcars") {
      mtcars
    } else {
      iris
    }
  })

  output[["rows"]] <- renderPrint({ input[["tbl_order"]] })

  output[["tbl"]] <- renderTable(Dat(), rownames = TRUE)
}

shinyApp(ui, server)

With the app below, using a renderUI reacting to the radio button, this is slightly better: this works as expected sometimes.

library(shiny)
library(shinyjqui)

ui <- fluidPage(
  radioButtons("radio", "Data", choices = c("mtcars", "iris")),
  verbatimTextOutput("rows"),
  uiOutput("tblUI")
)

server <- function(input, output) {

  Dat <- reactive({
    if(input[["radio"]] == "mtcars"){
      mtcars
    }else{
      iris
    }
  })

  output[["rows"]] <- renderPrint({input[["tbl_order"]]})

  output[["tbl"]] <- renderTable(Dat(), rownames = TRUE)

  output[["tblUI"]] <- renderUI({
    Dat()
    sortableTableOutput("tbl")
  })

}

shinyApp(ui, server)

What can we do to get an app which correctly works? Maybe an alternative to shinyjqui::sortableTabbleOutput?

CodePudding user response:

You are close, but a little more needs to be done. This will work:

library(shiny)
library(shinyjqui)

ui <- fluidPage(
    radioButtons("radio", "Data", choices = c("mtcars", "iris")),
    verbatimTextOutput("rows"),
    uiOutput("tblUI")
)

server <- function(input, output) {
    
    Dat <- reactive({
        if(input[["radio"]] == "mtcars"){
            tbl_id(0)
            mtcars
        }else{
            tbl_id(1)
            iris
        }
    })
    tbl_id <- reactiveVal(0)
    output[["rows"]] <- renderPrint({input[[paste0("tbl", tbl_id(), "_order")]]})
    
    observe({
        output[[paste0("tbl", tbl_id())]] <- renderTable(Dat(), rownames = TRUE)
    })

    output[["tblUI"]] <- renderUI({
        Dat()
        sortableTableOutput(paste0("tbl", tbl_id()))
    })
}

shinyApp(ui, server)

Let me explain why.

  1. In the sortableTableOutput function, you can see there is some JS code that will be run. The script binds the table to a sortable event and binds to shiny. Not entirely sure how jqui sets shiny input, but from this $.map(idx ... I guess the listener is added to each row element.
  2. Then, here comes the second problem. If you set up the new table rows with sortable event, it has a target shinyjqui.msgCallback({"ui":"[id='tbl']". This is the same ID as the old table. Both tables' rows are pointing to the same ID, so ID conflicting usually causes trouble. This is my guess why it gives you NULL.
  3. Once we know the cause, the solution is pretty straightforward. We bind two tables to different IDs, when generating the container, we just name them differently, and it worked! As an experienced Shiny user like you, I believe there is no need to explain the code details.
  4. The destroy option you suggested will not work, because the sortable is applied on each row, you would need to apply nrow times of destroy, and it cannot avoid the ID conflict problem.

enter image description here

I think this is a critical feature that this package is missing. I would recommend filing an issue on Github.

  • Related