Search code examples
rshinyshinyjs

Shiny DTedit, show or hide insert/new button based on rows_selected status in second DTedit table


I have two DTedit tables which are functionally related

I do not want users to get the Insert/New button in DT#2 when no row is selected in DT#1

I have Table1_Results$rows_selected to test if selection exists (length>0)

I also identified the id of the 'New button' in DT#2 as being Table2_add

But do not succeed to make the length of Table1_Results$rows_selected trigger the shinyjs show() or hide() action for DT#2

Could anyone please share some reactivity command to do this!

the following code is not working but illustrates my aim

observe(Table1_Results$rows_selected,{
    if (length(Table1_Results$rows_selected)) {
        shinyjs::show('Table2_add')
    } else {
        shinyjs::hide('Table2_add')
    }
})

Error in .getReactiveEnvironment()$currentContext() : Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)

This manual test using a button works

observeEvent(input$showhide, {
    toggle('Table2_add')
})

So it is really the reactive testing of the Table1_Results$rows_selected which is lacking

Thanks in advance


In the code below:

  • I cannot clear the selected row in the observed textoutput
  • I do not succeed to hide the New button
Note: I use DTedit because it allows other features not shown here

AIMs: 
1) when no drink is selected, hide the New button for containers
2) manage <table>$rows_selected so that it reflects the current status
library("shiny")
library("shinyjs")
library("DT")
library("DTedit")

server <- function(input, output) {
    
    Drink_Results <- dtedit(
        input, output,
        name = 'Drink',
        thedata = data.frame(
            ID = c(1:3),
            drink = c('Tea', 'Coffea', 'Water'),
            stringsAsFactors = FALSE
        )
    )

    # create proxy to clear row selection (found 'Drinkdt' by looking in the source)
    Drink_proxy <- DT::dataTableProxy('Drinkdt')
    
    Container_Results <- dtedit(
        input, output,
        name = 'Container',
        thedata = data.frame(
            ID = c(1:3),
            Container = c('Cup', 'Glass', 'Pint'),
            stringsAsFactors = FALSE
        )
    )

    # create proxy to clear row selection    
    Container_proxy <- DT::dataTableProxy('Container')

    # manually toggle visibility for New button
    observeEvent(input$showhide, {
        shinyjs::toggle('Container_add')
    })
    
    # clear Drink row selection 
    observeEvent(input$clearrows, {
        Drink_proxy %>% selectRows(NULL)
    })

    # when no drink is selected, hide the New button for containers    
    observeEvent(Drink_Results$rows_selected, {
        if ( length(Drink_Results$rows_selected) ) {
            shinyjs::show('Container_add')
        } else {
            shinyjs::hide('Container_add')
        }
    })
    
    # attempt to react on clearing the row-selection
    choice <- reactive({
        paste0(Drink_Results$rows_selected, " - ", Container_Results$rows_selected)
    })
    
    # output current combination
    output$choice <- renderText({ as.character(choice()) })
    
}

ui <-  tagList(useShinyjs(),
               fluidPage(
                   shinyFeedback::useShinyFeedback(),
                   
                   h3('What will you drink?'),
                   uiOutput('Drink'),
                   
                   # manually clear row selections
                   actionButton(inputId="clearrows", label="clear selected drink", icon=icon('trash')),
                   
                   hr(),
                   
                   h3("What container do you prefer?"),
                   uiOutput('Container'),
                   
                   hr(),
                   
                   # manually hide the New button
                   actionButton(inputId="showhide", label="toggle New buttons", icon=icon('refresh')),
                   
                   hr(),
                   
                   # show current user choices
                   textOutput('choice'),
                   
               )
)

shinyApp(ui = ui, server = server)

Solution

  • The reactive for selected row is input$Drinkdt_rows_selected in your case, based on the source code. If you use that, your code works fine. Try this

    server <- function(input, output) {
      
      ##  could not install DTedit. So, made a copy of the function
      source("C:\\RStuff\\GWS\\dtedit.R", local=TRUE)
    
      Drink_Results <- dtedit(
        input, output,
        name = 'Drink',
        thedata = data.frame(
          ID = c(1:3),
          drink = c('Tea', 'Coffea', 'Water'),
          stringsAsFactors = FALSE
        )
      )
      name <- "Drink"
    
      # create proxy to clear row selection (found Drinkdt by looking in the source)
      Drink_proxy <- DT::dataTableProxy('Drinkdt')
    
      Container_Results <- dtedit(
        input, output,
        name = 'Container',
        thedata = data.frame(
          ID = c(1:3),
          Container = c('Cup', 'Glass', 'Pint'),
          stringsAsFactors = FALSE
        )
      )
    
      # create proxy to clear row selection
      Container_proxy <- DT::dataTableProxy('Container')
    
      # clear Drink row selection
      observeEvent(input$clearrows, {
        Drink_proxy %>% selectRows(NULL)
        shinyjs::hide('Container_add')
      })
      
      sel <- reactive({!is.null(input[[paste0(name, 'dt_rows_selected')]])}) 
      observe({
        print(sel())
        print(input$Drinkdt_rows_selected)
      })
    
      # when no drink is selected, hide the New button for containers
      observe({
      #observeEvent(input[[paste0(name, 'dt_rows_selected')]], {
        if ( length(input[[paste0(name, 'dt_rows_selected')]])>0 ) {
          shinyjs::show('Container_add')
        }else {
          shinyjs::hide('Container_add')
        }
      })
      
      observeEvent(Drink_Results$thedata, {
        message(Drink_Results$thedata)
      })
    
      observeEvent(input[[paste0(name, 'dt_rows_selected')]], ignoreNULL = FALSE, {
        # 'no' (NULL) row will be 'selected' after each edit of the data
        message(paste("Selected row:", input[[paste0(name, 'dt_rows_selected')]]))
      })
    
    
      # attempt to react on clearing the row-selection
      choice <- reactive({
        if (is.null(input[[paste0(name, 'dt_rows_selected')]])) {
          paste0("Drink not selected")
        }else {
          paste0(input[[paste0(name, 'dt_rows_selected')]], " - ", input$Containerdt_rows_selected)
        }
        
      })
    
      observeEvent(input$showhide, {
        toggle('Container_add')
      })
      
      # output current combination
      output$choice <- renderText({ choice() })
    }
    
    ui <- fluidPage(
      shinyFeedback::useShinyFeedback(),
      useShinyjs(),
      h3('What will you drink?'),
      uiOutput('Drink'),
    
      # manually clear row selections
      actionButton(inputId="clearrows", label="clear selected drink", icon=icon('trash')),
    
      hr(),
    
      h3("What container do you prefer?"),
      uiOutput('Container'),
    
      hr(),
    
      # manually hide the New button
      actionButton(inputId="showhide", label="toggle New buttons", icon=icon('refresh')),
    
      hr(),
    
      # show current user choices
      textOutput('choice'),
    
    )
    
    shinyApp(ui = ui, server = server)