I have a mix of dynamically created inputs and normally defined inputs. The dynamically created inputs behave like a large vertically stacked block that the other inputs fl
All four dynamic generated widgets are treated as one object because they are contained in a list. Therefore it is quite difficult to position them.
The easiest solution I can think of is to use insertUI
function from the newest shiny version which can be installed using devtools
package:
devtools::install_github("rstudio/shiny")
(Here you can read/learn more about insertUI
and removeUI
)
We have to tell insertUI
where it should add new widgets to the user interface, and we do it by specifying the parameter selector
(it has to be a string that is accepted by jQuery selector) and the parameter where
(there are four choices which which specify how widgets should go relative to the selector)
So if we want to add a new widget under Field 1
we have to set selector = "#fielda"
(We use #
selector since we are referring to the ID). Similarly, we add another widget under Field 2
by setting selector = "#fieldb"
and so on. Following your naming convention we can generalize it to:
selector = paste0('#field', letters[i])
In all cases we also set where = "afterEnd
. It will position new widgets below reference widgets. (There is no space to position them above reference widgets)
Now we can wrap insertUI
into a for-loop
for (i in 1:4) {
insertUI(
selector = paste0('#field', letters[i]),
where = "afterEnd",
ui = textInput(paste0('field',i), paste('Field',i),'')
)
}
Note that we don't need to add any uiOutput
on the client side.
The other solution would be to render whole dashboardBody
on the server side creating dynamically distinct uiOutput
s...which wouldn't be pleasant.
When we take a look at the picture we can see that I did it the other way around - capital letters in names in the first row and numbers in names in the second row. It shouldn't be a big deal I hope :)
Full example:
library(shiny)
library(shinydashboard)
shinyApp(
ui = dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
flowLayout(
textInput('fielda','Field A',''),
textInput('fieldb','Field B',''),
textInput('fieldc','Field C',''),
textInput('fieldd','Field D','')
)
)
),
server = function(input, output) {
for (i in 1:4) {
insertUI(
selector = paste0('#field', letters[i]),
where = "afterEnd",
ui = textInput(paste0('field',i), paste('Field',i),'')
)
}
}
)
EDIT:
If there are no reference widgets then you can also dynamically define flowLayout
on the server side using non-standard evaluation.
Say, you want to generate three textInput
widgets. You would normally do this by typing
flowLayout(
textInput('fielda', 'Field A'),
textInput('fieldb', 'Field B'),
textInput('fieldc', 'Field C')
)
on the client side. You can do the same thing by pasting strings with paste0
function on the server side,
i = 1:3
UI <- paste0("flowLayout(",
paste0("textInput(",
"'field", letters[i], "', ",
paste0("'Field ", LETTERS[i], "'"),
")",
collapse = ", "),
")")
saving it in a variable, say, UI
, then parsing string and finally evaluating it.
eval(parse(text = UI))
In the example below you have 15 dynamically generated widgets
Full example:
library(shiny)
library(shinydashboard)
shinyApp(
ui = dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
wellPanel(
uiOutput("ui1")
)
)
),
server = function(input, output) {
output[["ui1"]] <- renderUI({
i = 1:15
UI <- paste0("flowLayout(",
paste0("textInput(",
"'field", letters[i], "', ",
paste0("'Field ", LETTERS[i], "'"),
")",
collapse = ", "),
")")
# print(UI)
eval(parse(text = UI))
})
}
)