The dataFilter module renders an interactive table with
a filter box on every column and returns only the rows the user
has left visible. Because it hands that filtered data back as a
reactive, you can plug it in front of any plotting module so the plot
always reflects the current table filters.
Like every module in VizModules, it comes in two
halves that share an id:
dataFilterUI(id) – places the table in your UI.dataFilterServer(id, data, ...) – runs the table and
returns the filtered data.The server is small, and its job is easy to describe:
factor.char.cols = TRUE) every character column is
converted to a factor. This is purely cosmetic: it makes DT show a
drop-down select filter for those columns instead of a free-text search
box.filter = "top").input$table_rows_all. The module subsets the data to those
rows, calls droplevels() so filtered-out categories don’t
linger in plot legends or axes, and returns the result as a
reactive.Conceptually:
data (reactive) --> character cols to factors --> DT table (user filters)
|
input$table_rows_all (visible rows)
v
filtered data (reactive)
| Argument | Default | Purpose |
|---|---|---|
id |
– | Module id; must match the id given to
dataFilterUI(). |
data |
– | A reactive containing the data frame to display and filter. |
factor.char.cols |
TRUE |
Convert character columns to factors so they get drop-down filters. |
page.length |
10 |
Rows shown per page. |
col.visibility |
FALSE |
Add a “Columns” button so users can show/hide individual columns. |
hide.columns |
NULL |
Column names (or positions) to hide when the table is first drawn. |
A reactive that evaluates to the filtered subset of
data. This is the key to the module’s flexibility: a
reactive can be read by as many consumers as you like, so the same
filtered data can drive one plot or several.
Wide data frames make for a busy table and a wall of filter boxes.
Pass the columns you don’t want on screen to hide.columns
and the table is drawn without them – no column, no filter box:
filtered <- dataFilterServer("filter", reactive(example_bar),
hide.columns = c("internal_id", "batch")
)Two things to keep in mind:
col.visibility = TRUE to get DT’s “Columns” button, which
lets users switch any column – including the ones you hid – back
on.filtered <- dataFilterServer("filter", reactive(example_bar),
hide.columns = c("internal_id", "batch"),
col.visibility = TRUE
)The name-to-position lookup behind hide.columns is
exported as resolve_column_targets(), so you can reach for
it in your own tables too. DataTables addresses columns in
columnDefs by zero-based position; this turns column names
into those positions (pass rownames = TRUE if your table
shows a row-names column, which shifts everything over by one). Hiding
is just one use - the same targets drive width,
orderable, className, and friends:
Pass the returned reactive straight into a plotting module’s
data argument. When the user filters the table, the plot
redraws automatically.
library(shiny)
library(VizModules)
ui <- fluidPage(
plotthis_BarPlotOutputUI("bar"),
dataFilterUI("filter"),
plotthis_BarPlotInputsUI("bar", example_bar)
)
server <- function(input, output, session) {
# Returns a reactive of only the currently-visible rows.
filtered <- dataFilterServer("filter", reactive(example_bar))
# The plot tracks the filter automatically.
plotthis_BarPlotServer("bar", data = filtered)
}
if (interactive()) shinyApp(ui, server)Note the shared id: "bar" is used for the
module’s OutputUI, InputsUI, and
Server, while "filter" ties
dataFilterUI() to dataFilterServer().
Because the filtered data is just a reactive, you don’t need any
special machinery to connect it to more than one plot – feed the
same filtered reactive into each module’s server.
Every module reading it re-renders whenever the filter changes, so a
single table keeps all of the plots in sync.
library(shiny)
library(shinyjs)
library(VizModules)
ui <- fluidPage(
useShinyjs(),
fluidRow(
column(6, plotthis_BarPlotOutputUI("bar")),
column(6, plotthis_BoxPlotOutputUI("box"))
),
dataFilterUI("filter"),
fluidRow(
column(6, plotthis_BarPlotInputsUI("bar", example_bar)),
column(6, plotthis_BoxPlotInputsUI("box", example_bar))
)
)
server <- function(input, output, session) {
# One filtered reactive, shared by both modules.
filtered <- dataFilterServer("filter", reactive(example_bar))
plotthis_BarPlotServer("bar", data = filtered)
plotthis_BoxPlotServer("box", data = filtered)
}
if (interactive()) shinyApp(ui, server)The only rule to remember is namespacing: each plotting
module’s InputsUI, OutputUI, and
Server must share the same top-level
id ("bar" and "box"
above). Nothing wraps the module servers, so their ids resolve to the
top-level namespace where their UI lives, and everything connects.
A ready-to-run version of this app ships with the package:
createModuleApp()The *App() helpers and createModuleApp()
already wire a dataFilter table to a single plot for you
(see the Quick Start vignette). Reach for
dataFilterUI() / dataFilterServer() directly
when you want to control the layout yourself – for example, to share one
table across multiple plots as shown above, or to embed the table inside
a larger custom module.
dataFilterServer() returns a reactive of the
currently-visible rows.data
argument.InputsUI, OutputUI, and
Server on a shared, top-level id so namespaces
line up.