1

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)
CallumH
  • 751
  • 1
  • 7
  • 22

2 Answers2

1

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")
    })

  }
)
dk.
  • 2,030
  • 1
  • 22
  • 22
0

I was facing the same issue to update the values and text in the bs4Dash::descriptionBlock(); the solution I found was to use the textOutput() elements in the descriptionBlock(), this way I can control and update the textOutput() with any value I want from the server side with a simple renderText().

Your code would look like this:

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 = "Box to be updated",
        id = "mybox2",
        collapsible = F,
        closable = F,
        width = 3,
        footer = fluidRow(
          column(
            width = 6,
            descriptionBlock(
              number = textOutput("number_to_update"),
              numberColor = 'success',
              numberIcon = icon("caret-up"),
              header = NULL,
              text = textOutput("text_stuff"),
              rightBorder = FALSE,
              marginBottom = FALSE
            )
          )
        )
      )
    )
  )
)

server <- function(input, output) {
  
  # this can come from any object you have (df, etc)
  # just add a reactive({}) to perform the reactive operations
  some_value <- 100
  some_text <- "Original text"
  
  output$number_to_update<- renderText({
    some_value 
  })
  
  output$text_stuff <- renderText({
    some_text
  })
  
  observeEvent(input$btn_update, {
    
    some_value <- -999
    some_text <- "Updated text"
    
    output$number_to_update<- renderText({
      some_value 
    })
    
    output$text_stuff <- renderText({
      some_text
    })
  })
  
}

shinyApp(ui, server)

This way you can control the descriptionBlock() elements from the server side.

I also updated your UI because the proper way to use a descriptionBlock() inside a box element is in the footer argument.

Hope this helps you with your current and future apps :)