rhtml-listshtmltools

How to create an adaptative list (ul+li) using htmltools?


In the context of a Shiny application, I'm trying to display warnings thrown by some call as an HTML list.

I could get all warnings as a character vector thanks to this answer, and I'm now actually building the HTML list.

The easiest way I could come up with is this code:

warns = c("warning 1", "warning 2", "warning 3", "warning 4")
paste0("<ul><li>", paste0(warns, collapse = "</li><li>"), "</li></ul>") %>% HTML

Inside shiny::renderUI, this code outputs well, but it is not very clean, and even after thorough testing I still find it somehow error-prone.

Therefore, I'd like to use the package htmltools to build the list, as it seems to be the recommended way of writing HTML with R.

However, tags$li can only handle strings and not character vectors, and tags$ul uses the ellipsis instead of list or vectors so even using loops I couldn't build my list.

What is the best way to build an HTML list using htmltools?


Solution

  • There are a couple of ways to build this list. Depending on what the aim of the application is, and if you want to run additional logic that controls the HTML markup, you can write list items using the children attribute of an element ul$children or run it one go.

    The markup for child elements can be done using base apply functions (i.e., lapply is probably the better option as you want to return a shiny tags object). Alternatively, the purrr package is good and a bit faster. However, I would consider pre-generating the markup if you are rendering hundreds of elements.

    Using the ...$children approach, Here's how you could generate the html.

    library(htmltools)
    warns <- c("warning 1", "warning 2", "warning 3", "warning 4")
    
    ul <- tags$ul()
    ul$children <- lapply(warns, function(x) tags$li(x))
    

    If you wanted to generate the markup in one run, you can do it like so.

    ul <- tags$ul(lapply(warns, function(x) tags$li(x)))
    

    EDITS: I forgot to add the examples using the purrr package

    # using ul$children
    ul <- tags$ul()
    ul$children <- purrr::map(warns, function(.x) tags$li(.x))
    
    # generating everything at once
    ul <- tags$ul(
        purrr::map(warns, function(.x) tags$li(.x))
    )