'Why is updateTabsetPanel running last inside an observeEvent?

I am looking to:

  • Insert UI elements
  • Update UI elements
  • Run a long calculation

All after pressing one button. Example app:

#EXAMPLE APP----
#Libraries----
library(shiny)
library(shinyjs)

#UI code----
ui <- fluidPage(
  useShinyjs(),
  mainPanel(
    tabsetPanel(
      id = "test",
      tabPanel(
        title = "First",
        actionButton(
          "press",
          "Press Me")),
      tabPanel(
        title = "Second",
        tabsetPanel(
          id = "toinsert"
        )))
  )
  
)

#Server code----
server <- function(input, output, session) {
  ##Disable as default Second tab----
  observe({
    disable(selector = '#test a[data-value = "Second"]')
    })
  
  ##Enable select 2ndtab and insert-select tab inside----
  totest <- reactiveValues()
  totest$tab <- 0
  observeEvent(input$press,{
    enable(selector = '#test a[data-value = "Second"]')
    
    updateTabsetPanel(
      session,
      'test',
      selected = "Second"
    )
    
    totest$tab <- totest$tab + 1
    insertTab(
      session = session,
      inputId = "toinsert",
      tab = tabPanel(
        title = paste0("AA_",totest$tab),
        div(p("BBBBB"))
      )
    )
    
    updateTabsetPanel(
      session,
      'toinsert',
      selected = paste0("AA_",totest$tab)
    )
    
    totest$a <- 1    
    
  })
  
  ##Running long calculation once proper tab is selected(not working)----
  observeEvent(totest$a,{
    if(totest$a == 1){
      Sys.sleep(5)
      print("A")
      totest$a <- 0
    }else{}
    
  })
}

shinyApp(ui, server)

Running the previous example you will notice that the code runs correctly but out of order: running the calculation first and updating UI elements after. Searching on stackoverflow I found a similar question R shiny: Update tabsetpanel before finishing all the observeEvent code and tried the accepted answer changing my observeEvent code to:

observeEvent(input$press,{
    enable(selector = '#test a[data-value = "Second"]')
    
    updateTabsetPanel(
      session,
      'test',
      selected = "Second"
    )
    
    totest$tab <- totest$tab + 1
    insertTab(
      session = session,
      inputId = "toinsert",
      tab = tabPanel(
        title = paste0("AA_",totest$tab),
        div(p("BBBBB"))
      )
    )
    
    updateTabsetPanel(
      session,
      'toinsert',
      selected = paste0("AA_",totest$tab)
    )
    
    
    givingvalue <- reactive({
      req(input$toinsert)
      totest$a <- 1
    })
    
    givingvalue()
    
  })

But this didn't work. After clicking the button for the first time the UI is updated but the long calculation didn't run and after I press the button for the second time it works as previous configuration. I assume is running the calculation after button is clicked again.

Why is this happening? How could I fix my problem?



Solution 1:[1]

As I said on the comments of YBS's answer what actually worked for me was changing the second observeEvent to check on input$toinsert instead of totest$a. Being the final code as follow.

#EXAMPLE APP----
#Libraries----
library(shiny)
library(shinyjs)

#UI code----
ui <- fluidPage(
  useShinyjs(),
  mainPanel(
    tabsetPanel(
      id = "test",
      tabPanel(
        title = "First",
        actionButton(
          "press",
          "Press Me")),
      tabPanel(
        title = "Second",
        tabsetPanel(
          id = "toinsert"
        )))
  )
  
)

#Server code----
server <- function(input, output, session) {
  ##Disable as default Second tab----
  observe({
    disable(selector = '#test a[data-value = "Second"]')
  })
  
  ##Enable select 2ndtab and insert-select tab inside----
  totest <- reactiveValues()
  totest$tab <- 0
  observeEvent(input$press,{
    enable(selector = '#test a[data-value = "Second"]')
    
    updateTabsetPanel(
      session,
      'test',
      selected = "Second"
    )
    
    totest$tab <- totest$tab + 1
    insertTab(
      session = session,
      inputId = "toinsert",
      tab = tabPanel(
        title = paste0("AA_",totest$tab),
        div(p("BBBBB"))
      )
    )
    
    updateTabsetPanel(
      session,
      'toinsert',
      selected = paste0("AA_",totest$tab)
    )
    
    totest$a <- 1    
    
  })
  
  ##Running long calculation once proper tab is selected(working)----
  observeEvent(input$toinsert,{
    if(totest$a == 1){
      Sys.sleep(5)
      print("A")
      totest$a <- 0
    }else{}
    
  })
}

shinyApp(ui, server)

Solution 2:[2]

Perhaps you are looking for this

ui <- fluidPage(
  useShinyjs(),
  mainPanel(
    tabsetPanel(
      id = "test",
      tabPanel(
        title = "First",
        actionButton(
          "press",
          "Press Me")),
      tabPanel(
        title = "Second",
        tabsetPanel(
          id = "toinsert"
        )))
  )
  
)

server <- function(input, output, session) {
  # observe({
  #   disable(selector = '#test a[data-value = "Second"]')
  # })
  
  totest <- reactiveValues(tab=0,a=0)

  observeEvent(input$press,{
    #enable(selector = '#test a[data-value = "Second"]')
    totest$tab <- totest$tab + 1
    
    updateTabsetPanel(
      session,
      'test',
      selected = "Second"
    )
    
  })
  
  observeEvent(input$test,{
    print(input$test)
    #totest$a <- 1
    if (input$test=="Second") {
      insertTab(
        session = session,
        inputId = "toinsert",
        tab = tabPanel(
          title = paste0("AA_",totest$tab),
          div(p("BBBBB"))
        )
      )
      
      updateTabsetPanel(
        session,
        'toinsert',
        selected = paste0("AA_",totest$tab)
      )
    }
    
  })
  
  observeEvent(input$toinsert,{
    if (!is.null(input$toinsert)) {
        Sys.sleep(5)
        print("A")
    }
  }, ignoreNULL = TRUE)
  
}

shinyApp(ui, server)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 David Díaz
Solution 2