Two closely related arguments — defaults and
hide.inputs/hide.tabs — let you control what
users see and what the module starts with. This vignette covers both in
depth.
defaults: pre-filling inputsPass a named list to the defaults argument of any
*InputsUI() call. Each name is an input ID (matching the
underlying plot function argument), and its value is what the control
initialises to.
library(VizModules)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
dittoViz_scatterPlotInputsUI(
"p", mtcars,
defaults = list(
x.by = "wt",
y.by = "mpg",
color.by = "cyl",
size = 3,
best.fit = TRUE
)
)
),
mainPanel(dittoViz_scatterPlotOutputUI("p"))
)
)
server <- function(input, output, session) {
dittoViz_scatterPlotServer("p", data = reactive(mtcars))
}
shinyApp(ui, server)Defaults keys map directly to the argument names of the underlying
plot function. The quickest way to find them is the module’s
*InputsUI() help page,
e.g. ?dittoViz_scatterPlotInputsUI. The Plot
parameters and defaults section lists every wired argument, its
UI label, and its built-in default.
For inputs that come from the uniform tab helpers (Axes, Legend,
Lines, Plotly), see ?uniform_axes_inputs_ui,
?uniform_legend_inputs_ui,
?uniform_lines_inputs_ui, and
?uniform_plotly_inputs_ui respectively.
get_default() (used internally by every module) accepts
an optional validator predicate. If the value you supply
fails validation — e.g. passing a string where a logical is expected —
the module silently falls back to its built-in default rather than
erroring. This means a typo in a key is silent; double-check key names
against the help page if a default appears to have no effect.
Sometimes a parameter needs to follow your app rather than
be fixed once. A common case is a colour mapping that should track a
column the user has selected elsewhere in the app. For this, any
individual entry in defaults may be a
reactive() or a reactiveVal() instead of a
plain value:
library(VizModules)
ui <- fluidPage(
selectInput("colour_col", "Colour by", choices = c("cyl", "gear", "carb")),
sidebarLayout(
sidebarPanel(uiOutput("controls")),
mainPanel(dittoViz_scatterPlotOutputUI("p"))
)
)
server <- function(input, output, session) {
plot_defaults <- list(
x.by = "wt",
y.by = "mpg",
color.by = reactive(input$colour_col)
)
output$controls <- renderUI({
dittoViz_scatterPlotInputsUI("p", mtcars, defaults = plot_defaults)
})
dittoViz_scatterPlotServer("p", data = reactive(mtcars), defaults = plot_defaults)
}
shinyApp(ui, server)Pass the same list to both *InputsUI() and
*Server(), as you would for static defaults. Because the UI
needs access to the reactive, build it inside renderUI()
(the UI seed is taken with isolate(), so this does not
cause the controls to re-render whenever the reactive changes).
You get three guarantees:
Semantics worth knowing:
reactive() and reactiveVal()
are recognised. A plain function is treated as a literal
default value, not as something to call.custom.models in the
scatter module is the one to watch — will not re-display it. Reactive
defaults are not supported for that input.update*Input() from the parent?The obvious alternative is an observeEvent() in the
parent calling updateTextInput(session, "p-main", ...).
That works, but update*Input() is an asynchronous
round-trip to the browser, so each change renders the plot
twice: once with the stale value, then again when the
new value arrives back from the client. Reactive defaults exist to avoid
that second render. dev-notes/reactive-defaults-repro.R in
the package source runs both approaches side by side with render
counters if you want to see the difference.
createModuleApp() and
*App()defaults is forwarded all the way through the app
factory:
The same defaults list is accepted by
createModuleApp() directly:
app <- createModuleApp(
inputs_ui_fn = plotthis_BoxPlotInputsUI,
output_ui_fn = plotthis_BoxPlotOutputUI,
server_fn = plotthis_BoxPlotServer,
data_list = list("iris" = iris),
defaults = list(x.by = "Species", y.by = "Sepal.Length")
)
if (interactive()) runApp(app)hide.inputs: hiding individual controlsPass a character vector of input IDs to hide.inputs on
the server function. Those controls are hidden from the
UI while their values are still initialised (from defaults
if supplied, otherwise the built-in default) and passed to the plot on
every render.
server <- function(input, output, session) {
dittoViz_scatterPlotServer(
"p",
data = reactive(mtcars),
hide.inputs = c("shape.by", "plot.order", "opacity")
)
}Hidden inputs reflow: the surrounding controls close the gap rather
than leaving an empty space. This is handled automatically by the
flexbox grid that organize_inputs() creates.
A common pattern is to fix a column mapping or aesthetic so that app users cannot change it, while still applying it to every plot:
server <- function(input, output, session) {
dittoViz_scatterPlotServer(
"p",
data = reactive(mtcars),
defaults = list(color.by = "cyl"),
hide.inputs = "color.by"
)
}The colour mapping is always cyl, and the control for it
never appears.
This composes with reactive defaults: a hidden control whose default
is a reactive() still drives the plot, because the value is
resolved server-side rather than read back from the (now invisible)
input. That is the cleanest way to lock a parameter to app state
entirely.
hide.tabs: hiding entire tab panelsModules organise their inputs into named tabs
(e.g. "Data", "Points", "Lines",
"Axes", "Legend", "Plotly"). Pass
a character vector of tab names to hide.tabs to remove
whole groups at once.
server <- function(input, output, session) {
dittoViz_scatterPlotServer(
"p",
data = reactive(mtcars),
hide.tabs = c("Plotly", "Lines", "Trajectory")
)
}All inputs in a hidden tab are still initialised and active — they just aren’t shown. This is useful when you want to keep the defaults for an entire feature group (e.g. plotly export settings) without exposing the controls to users.
Tab names are module-specific. Inspect the inputs list
inside the relevant *InputsUI() source, or open
?<module>InputsUI and look for the tab headings
described there. Common tabs across most modules include:
| Tab | Contents |
|---|---|
"Data" |
Column selectors (x, y, color, split, etc.) |
"Axes" |
Title font, gridlines, tick styling |
"Legend" |
Legend title and text sizes |
"Lines" |
Reference lines (h/v/ablines) |
"Plotly" |
Download format, margins, drawn shape styling |
"Facet" |
Facet rows/columns, scales, subplot spacing |
defaults, hide.inputs, and
hide.tabsThese three arguments compose freely. A typical production pattern is to set defaults for everything the user shouldn’t touch, hide the individual controls that need fine-grained locking, and hide any whole tabs that are irrelevant for your use case:
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
plotthis_ViolinPlotInputsUI(
"v", example_rnaseq,
defaults = list(
x.by = "condition",
y.by = "expression",
color.by = "condition"
)
)
),
mainPanel(plotthis_ViolinPlotOutputUI("v"))
)
)
server <- function(input, output, session) {
plotthis_ViolinPlotServer(
"v",
data = reactive(example_rnaseq),
hide.inputs = c("color.by"),
hide.tabs = c("Plotly", "Lines")
)
}
shinyApp(ui, server)Here color.by is fixed to "condition" and
hidden. The entire "Plotly" and "Lines" tabs
are removed because they aren’t relevant to this app.
hide.inputs in createModuleApp() and
*App()Both accept hide.inputs and hide.tabs,
forwarding them to the server if it supports those arguments:
plotthis_ViolinPlotApp(
defaults = list(x.by = "Species", y.by = "Sepal.Length"),
hide.inputs = "color.by",
hide.tabs = "Plotly"
)When building a wrapper module (see
vignette("custom-modules", package = "VizModules")), you
can hide and show inputs at runtime in response to other inputs using
shinyjs:
library(shinyjs)
myModuleServer <- function(id, data_reactive) {
moduleServer(id, function(input, output, session) {
# Hide the 'size' input whenever a size.by column is chosen
observe({
if (nzchar(input$size.by)) {
shinyjs::hide(id = "size")
} else {
shinyjs::show(id = "size")
}
})
})
dittoViz_scatterPlotServer(id, data_reactive)
}Note that shinyjs::hide() / shinyjs::show()
act on the input element itself. For the reflow behaviour (no empty
gap), use the hide_input() / show_input()
helpers instead, which target the wrapping cell in the flexbox grid: