0

This picture is what happens when I try @Ben 's code.Right now, the input boxes boxes display one below another. I would like to be able to display them in a matrix such that there are input$numoc rows and input$inp1 columns.

UI

numericInput("cols", "Enter number of columns:",min=1,1),
numericInput("rows", "Enter number of rows:",min=1,1)

Server

output$matrixx <- renderUI({
    k= rep(c(1:input$rows), times = input$cols)
    mx<-max(input$rows,input$cols)
    lllist <- lapply(1:(input$cols*input$rows), function(i, j=((i-1)%/%input$rows)+1, y=k[[i]]) {
        iids <- paste("inp2", i, sep="")
        names<- paste("Treatment #",j," Outcome #",y,sep="")
        list(
            numericInput(iids,names, value=0, min=0, max=1, step=0.05)
        )
    })
    do.call(tagList, unlist(lllist, recursive = FALSE))
})
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
X L
  • 27
  • 5

2 Answers2

2

Sure, we can add fluidRow and column inside our renderUI to achieve this.

Here is a minimal example:

library(shiny)

ui <- fluidPage(
  
    numericInput("cols", "Enter number of columns:", min = 1, max = 4, 2), 
    numericInput("rows", "Enter number of rows:",    min = 1, 2),
    hr(),
    
    uiOutput("matrixx")
    
)

server <- function(input, output, session) {
  
    output$matrixx <- renderUI({
        
        ui_parts <- c()
        
        for(i in 1:input$rows){
            
            ui_parts[[i]] <- fluidRow(
                
                if(input$cols >= 1) {
                    column(3, 
                        textInput(
                            inputId = paste0("id", i + 10),
                            label   = paste0("id", i + 10) 
                        )
                    )
                },
                if(input$cols >= 2) {
                    column(3, 
                        textInput(
                            inputId = paste0("id", i + 20),
                            label   = paste0("id", i + 20) 
                        )
                    )
                },
                if(input$cols >= 3) {
                    column(3, 
                       textInput(
                           inputId = paste0("id", i + 30),
                           label   = paste0("id", i + 30) 
                       )
                    )
                },
                if(input$cols >= 4) {
                    column(3, 
                       textInput(
                           inputId = paste0("id", i + 40),
                           label   = paste0("id", i + 40) 
                       )
                    )
                }
            )
        }
        
        ui_parts

    })
    
}

shinyApp(ui, server)

For simplicity I limited it to 4 columns and wrote it in a more repetitive, but easier to read format. At the cost of extra complexity you could make it work with more columns, and shorten it by using additional loops.

Ash
  • 1,463
  • 1
  • 4
  • 7
  • thanks! How can I make this work for any number of columns? After 4 columns,they don't appear beside eachother anymore – X L Jan 20 '21 at 14:21
1

Here is one approach, dividing up rows and columns with two lapply statements. You can add column and set width either with a static value or dynamically depending on the number of elements.

I also added verbatimTextOutput to show the values entered in the numeric inputs as demonstration.

library(shiny)

ui <- fluidPage(
  numericInput("cols", "Enter number of columns:", min = 1, 1),
  numericInput("rows", "Enter number of rows:", min = 1, 1),
  verbatimTextOutput("values"),
  uiOutput("matrixx")
)

server <- function(input, output, session) {
  output$matrixx <- renderUI({
    lapply(seq(input$cols), function(i) {
      column(width = 4,
             lapply(seq(input$rows), function(j) {
               numericInput(paste0("inp2", i, "_", j), 
                            paste0("Treatment #", j, " Outcome #", i),
                            value = 0, min = 0, max = 1, step = 0.05)
             }))
    })
  })
  output$values = renderPrint({
    str(
      lapply(seq(input$cols), function(i) {
        lapply(seq(input$rows), function(j) {
          input[[paste0("inp2", i, "_", j)]]
        })
      })
    )
  })
}

shinyApp(ui, server)
Ben
  • 28,684
  • 5
  • 23
  • 45
  • thanks! How can I make this work for any number of columns? – X L Jan 20 '21 at 14:20
  • @XL This should work for any number of columns. Did it not provide the right setup when more columns are entered for the `numericInput` `input$cols`? Or please describe further what you need. – Ben Jan 20 '21 at 14:28
  • Once the number of columns hits 4, they start to appear underneath the first column. The app won't allow for more than 3 columns. Maybe there is a way to add a horizantal scroll bar to fix this problem? – X L Jan 20 '21 at 14:34
  • I included a picture in the original post – X L Jan 20 '21 at 14:37
  • Is there a max number of columns? There are 12 column width across, and above I arbitrarily specified a column width of 4 (so would fit 3 columns of 4 widths, 4x3=12). You could make a vector including what widths you'd want between 1 and 6 columns or so...or mathematically estimate what the width should be based on `input$cols`. Or, if you want small inputs, you could make `width = 2` which would work up to 6 columns across...or `width = 1` for 12 columns across... – Ben Jan 20 '21 at 14:39
  • is there a way to increase the width of the whole page so that I can have more than 12 columns? – X L Jan 20 '21 at 14:45
  • shiny is built on bootstrap with 12 columns, see [this](https://shiny.rstudio.com/reference/shiny/0.14/column.html) and [this question](https://stackoverflow.com/questions/55938158/shiny-use-more-than-12-columns-in-a-single-row). you could subdivide columns, or potentially you could have more flexibility with custom css. – Ben Jan 20 '21 at 15:08