简体   繁体   中英

Create dynamic number of card elements in shiny flowLayout

I like to fill an area in a shiny app with card elements. The items flow into the next row when there is not enough space. This can be achieved with flowLayout .

在此处输入图片说明

But I do not know the number of items in advance, so I need to create the card elements in a loop. But when I use lapply inside flowLayout all elements are shown below each other.

How to fix this, so that items are shown in rows next to each other?

library(shiny)

card <- function(.img, .species, .sepal.length) {
  HTML(
    paste0(
      '<div class="card">
      <img src="', .img, '" style="width:100%">
      <div class="container">
      <h4><i>', .species, '</i></h4>
      <hr>
      <p>Sepal Length: ', .sepal.length, '</p>
      </div>
      </div>')
  )
}

img.src <- "https://www.plant-world-seeds.com/images/item_images/000/007/023/large_square/iris_baby_blue.jpg?1500653527"

ui <- fluidPage(
  tags$head(tags$style('.card {
                         width: 250px;
                       clear: both;
                       /* Add shadows to create the "card" effect */
                       box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
                       transition: 0.3s;
                       }
                       /* On mouse-over, add a deeper shadow */
                       .card:hover {
                       box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
                       }
                       /* Add some padding inside the card container */
                       .container {
                       width: 250px;
                       padding: 2px 16px;
                       }')),
  uiOutput("cards")
)

server <- function(input, output, session) {

  # This looks as expected

  # output$cards <- renderUI({
  #   shiny::flowLayout(
  #     cellArgs = list(
  #       style = "
  #           width: auto;
  #           height: auto;
  #           margin: 5px;
  #           "),
  #     card(img.src,
  #          .species = iris[1, "Species"],
  #          .sepal.length = iris[1, "Sepal.Length"]),
  #     card(img.src,
  #          .species = iris[2, "Species"],
  #          .sepal.length = iris[2, "Sepal.Length"]),
  #     card(img.src,
  #          .species = iris[3, "Species"],
  #          .sepal.length = iris[3, "Sepal.Length"]),
  #     card(img.src,
  #          .species = iris[4, "Species"],
  #          .sepal.length = iris[4, "Sepal.Length"])
  #   )
  # })

  # Now elements are below each other when using lapply

  output$cards <- renderUI({
    shiny::flowLayout(
      cellArgs = list(
        style = "
        width: auto;
        height: auto;
        margin: 5px;
        "),
      lapply(1:4, function(.x) card(img.src,
                       .species = iris[.x, "Species"],
                       .sepal.length = iris[.x, "Sepal.Length"]))
      )
  })
}

shinyApp(ui, server)

The comment above links to an answer that works, but there is a much easier way.

Instead of using lapply (which makes a list, confusing flowLayout ), we can use do.call to expand the argument.

Updated server function:

server <- function(input, output, session) {

  output$cards <- renderUI({

    # First make the cards
    args <- lapply(1:4, function(.x) card(img.src,
                                  .species = iris[.x, "Species"],
                                  .sepal.length = iris[.x, "Sepal.Length"]))

    # Make sure to add other arguments to the list:
    args$cellArgs <- list(
      style = "
        width: auto;
        height: auto;
        margin: 5px;
        ")

    # basically the same as flowLayout(cards[[1]], cards[[2]],...)
    do.call(shiny::flowLayout, args)

  })
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM