htmlrshinyfunctional-programmingnumeric-input

lapply fails to assign label in creating numericInput within a function


Lapply with labels = names(x), returns weird shiny NULL HTML class; lapply with labels = as.character(names(x)), returns the correct HTML class but doesn't put a label. Any ideas why?

REPREX:

library(shiny) 

items <- list(
  "A" = "a",
  "B", = "b"
)

num_inputs <- function(items){
labels <- names(items)
temp. <- NULL 
for(i in 1:length(items)){
  temp. <- list(temp., numericInput(inputId = items[i],
               label = labels[i], 
               value = 1,
               min = 0,
               max = 10,
               step = 0.1)
  )
}
return(temp.)
}

# doesn't work 
num_inputs_fail <- function(items){
  lapply(items, FUN = function(x){ 
    numericInput(inputId = x,
                label = as.character(names(x)), 
                value = 1,
                min = 0,
                max = 10,
                step = 0.1)
    })
}

attempt1 <- tagList(num_inputs(items))
attempt2 <- tagList(num_inputs_fail(items))

Notice here (for just attempt1[1] and attempt[2] that the label is almost the same, but the actual label is missing!

<div class="form-group shiny-input-container">
  <label class="control-label" for="a">A</label>
  <input id="a" type="number" class="form-control" value="1" min="0" max="10" step="0.1"/>
</div>

<div class="form-group shiny-input-container">
  <label class="control-label" for="a"></label>
  <input id="a" type="number" class="form-control" value="1" min="0" max="10" step="0.1"/>
</div>

This page: Create dynamic number of input elements with R/Shiny

Includes the following code:

output$sliders <- renderUI({
  members <- as.integer(input$members) # default 2
  max_pred <- as.integer(input$max_pred) # default 5000
  lapply(1:members, function(i) {
    sliderInput(inputId = paste0("ind", i), label = paste("Individual", i),
                min = 0, max = max_pred, value = c(0, 500), step = 100)
  })
})

Inside a renderUI and the shiny app linked seems to correctly have the labels; but even using the paste() function inside my lapply doesn't fix it.

My R version: "R version 3.5.1 (2018-07-02)"

My Shiny version: '1.4.0'


Solution

  • lapply does not map the elements of the input list with their name.

    As suggested by @Ben, you can map names(items) instead of mapping items. An example:

    items <- list(
      "A" = "a",
      "B" = "b"
    )
    
    lapply(names(items), function(x) data.frame(id = x, label = items[[x]]))
    # [[1]]
    #   id label
    # 1  A     a
    # 
    # [[2]]
    #   id label
    # 1  B     b
    

    An alternative is to use purrr::imap:

    purrr::imap(items, ~ data.frame(id = .y, label = .x)) # .y: name  .x: value
    # $A
    #   id label
    # 1  A     a
    # 
    # $B
    #   id label
    # 1  B     b
    

    (imap stands for "indexed map").

    Or purrr::imap(items, function(value, name) data.frame(id = name, label = value)).