rshinybs4dash

How to Update a Shiny bs4Dash descriptionBlock server side


Can I have some guidance please in how to update the descriptionBlock in a shiny app with a bs4Dash dashboard? Thanks in advance.

I have tried multiple approaches but can’t seem to get the descriptionBlock values to change on the server and send to the UI; some have resulted in strange width behaviour and for that reason I have included a placeholder box to the left of width 9, beside my target box (width = 3) to the right.

It would seem that there should be an easy server side way to update these values and send to the UI but I just can’t find it. To keep it simple… I am looking to update on an event (actionButton click).

library(shiny)
library(bs4Dash)

ui <- dashboardPage(
  dashboardHeader(title = "Basic dashboard"),
  dashboardSidebar(),
  dashboardBody(
    fluidRow(
      column(12, actionButton('btn_update', 'UPDATE right box'))
    ),
    br(),
    fluidRow(
      box(
        title = textOutput("box_state"),
        "Box body",
        id = "mybox1",
        collapsible = F,
        closable = F,
        width = 9
      ),
      box(
        title = textOutput("box_state"),
        id = "mybox2",
        collapsible = F,
        closable = F,
        width = 3,
        descriptionBlock(
          number = '100',
          numberColor = 'success',
          numberIcon = icon("caret-up"),
          header = NULL,
          text = 'stuff',
          rightBorder = TRUE,
          marginBottom = FALSE
        )
      )
    )
  )
)

server <- function(input, output) {
  
  
  observeEvent(input$btn_update,{
    
    # How is this sent as an update to the UI please?
    descriptionBlock(
      number = '-999',
      numberColor = 'danger',
      numberIcon = icon("caret-down"),
      header = NULL,
      text = 'different stuff',
      rightBorder = TRUE,
      marginBottom = FALSE
    )
    
  })

}

shinyApp(ui, server)

Solution

  • There isn't an easy way. Some of the bs4Dash elements like box labels, description block and others are specialized decorative elements that only take static values. You can't insert shiny function to render reactive values. These bs4Dash elements also don't take an id, so it's difficult to manipulate them.

    One solution is to use htmltools::tagAppendAttributes to give the descriptionBlock an id and shinyjs::html to modify the parts.

    From the console to see its pieces, run

    tagAppendAttributes(id="block",
       descriptionBlock(number='100', 
                        numberColor='success',
                        numberIcon = icon("caret-up"),
                        text='stuff'))
    
    <div class="description-block border-right" id="block">
      <span class="description-percentage text-success">
        100
        <i class="fas fa-caret-up" role="presentation" aria-label="caret-up icon"></i>
      </span>
      <h5 class="description-header"></h5>
      <span class="description-text">stuff</span>
    </div>
    

    Use the JQuery selector to update the child elements, e.g.

    html(selector="#block > span.description-text", html="different stuff")
    

    Here's a working solution:

    library(shiny)
    library(bs4Dash)
    library(shinyjs)
    library(htmltools)
    
    shinyApp(
      ui = dashboardPage(
        dashboardHeader(tags$head(shinyjs::useShinyjs()),title = "Basic dashboard"),
        dashboardSidebar(),
        dashboardBody(
          fluidRow(
            column(12, actionButton('btn_update', 'UPDATE box'))
          ),
          fluidRow(
            box(
              width = 3,
              tagAppendAttributes(
                id="block",
                descriptionBlock(
                  number = '100',
                  numberColor = 'success',
                  numberIcon = icon("caret-up"),
                  header = NULL,
                  text = 'stuff',
                  rightBorder = FALSE,
                  marginBottom = FALSE
                )
              )
            )
          )
        )
      ),
    
      server=function(input, output) {
        observeEvent(input$btn_update,{
          html(selector="#block > span.description-text", html="different stuff")
          html(selector="#block > span.description-percentage", html='-999<i class="fas fa-caret-down"/>')
          removeCssClass(selector="#block > span.description-percentage", class="text-success")
          addCssClass(selector="#block > span.description-percentage", class="text-danger")
        })
    
      }
    )