Title: | Create Encapsulated Shiny Components |
---|---|
Description: | Create complex yet manageable Shiny applications with nestable components that encapsulate both UI and state. Heavily inspired by many JavaScript frameworks, particularly Vue. |
Authors: | Elian Thiele-Evans [aut, cre] |
Maintainer: | Elian Thiele-Evans <[email protected]> |
License: | GPL (>= 3) |
Version: | 0.0.0.9000 |
Built: | 2024-11-21 04:35:17 UTC |
Source: | https://github.com/ElianHugh/rsx |
Components represent the encapsulation of a shiny module's logic and UI into a singular object, and can be used like any other shiny tag.
For more information on the data, methods, template, and styles arguments, see related documentation.
component( name = NULL, data = NULL, methods = NULL, template = NULL, styles = NULL )
component( name = NULL, data = NULL, methods = NULL, template = NULL, styles = NULL )
name |
component name |
data |
a function that returns a named list of values, which are used to store the component's state |
methods |
named list of functions, which define the behavior of the component |
template |
function that returns a taglist |
styles |
function that returns a character vector or list of CSS styles that are scoped to the component |
Other components:
component-data
,
component-methods
,
component-styles
,
component-template
library(shiny) counter <- component( name = "counter", data = function() { list( label = "Counter", count = reactiveVal(0L) ) }, template = function(ns) { tagList( actionButton(ns("button"), label = self$label), verbatimTextOutput(ns("out")) ) }, methods = list( setup = function(input, output, session) { observeEvent(input$button, { self$count( self$count() + 1L ) }) output$out <- renderText( self$count() ) } ) ) tagList( tags$h1("Counter"), counter() )
library(shiny) counter <- component( name = "counter", data = function() { list( label = "Counter", count = reactiveVal(0L) ) }, template = function(ns) { tagList( actionButton(ns("button"), label = self$label), verbatimTextOutput(ns("out")) ) }, methods = list( setup = function(input, output, session) { observeEvent(input$button, { self$count( self$count() + 1L ) }) output$out <- renderText( self$count() ) } ) ) tagList( tags$h1("Counter"), counter() )
Data is used for a component's internal state and can also be used to pass information from a parent component to its children. Both component templates and methods have access to component data.
To create component data, define a function that returns a list of named objects. These objects can be Shiny reactive objects, data frames, lists, or any other R object.
For instance, the following is a valid data function:
function() { list( rctv = shiny::reactiveVal(), df = mtcars ) }
Data can be accessed in both the methods and template parts of the component via the self
keyword.
For example, the following component defines the data foo
, and accesses it in the template via self$foo
.
x <- component( name = "data_access", data = function() { list( foo = 1L ) }, template = function(ns) { self$foo } ) print(x()) #> <rsx::instance> `data_access` #> 1
To pass data to a component, use the data
argument when calling the
component function. The data
argument should be a list of named objects
that match the names of the objects defined in the component's data
function. For example:
x <- component( name = "data_access", data = function() { list( foo = 1L ) }, template = function(ns) { self$foo } ) print(x(data = list(foo = 2L))) #> <rsx::instance> `data_access` #> 2
Other components:
component-methods
,
component-styles
,
component-template
,
component()
Methods are a list of functions that are contained within a given component, and typically will manipulate component data or respond to user input.
Methods can be accessed in both other methods and the component template
via the self
keyword.
Methods are defined in the component definition as a named list of functions.
For example, the following is a method section from a simple counter component that defines two methods, setup and increment, which respectively set up the module server and increment the count state:
methods = list( setup = function(input, output, session) { output$txt <- renderText({ paste0("Count is: ", self$count()) }) observeEvent(input$button, { self$count(self$count() + 1L) }) }, increment = function() { self$count(self$count() + 1L) } )
The setup hook is defined by passing a function named "setup" to the methods list. Setup is used as the module server for the component. This method is called when the module is first initialized, and is used to set up input/output bindings and any other necessary initialization code.
setup = function(input, output, session) { }
The render
hook is defined by passing a function named "render" to
the methods list. The render hook allows for the modification of template code
prior to its rendering.
render = function(element) { }
Other components:
component-data
,
component-styles
,
component-template
,
component()
The styles argument is function that returns a character vector or list that defines the styles for the component. Styles are scoped to the component.
The styles argument takes a character vector of length 1 that defines the styles for the component. For example:
styles = function() { "a { color: red; }" }
This would define the CSS styles for the anchor
elements (<a>
) in the component, setting their color to red.
Styles defined in a component are scoped to the component, meaning they will only apply to elements within that component.
To style the top-level node of the component, we can apply the styles without specifying a tag:
x <- component( name = "scoped_styles", template = function(ns) { shiny::div( "Hello world!" ) }, styles = function() { "color: red" } )
Other components:
component-data
,
component-methods
,
component-template
,
component()
The template refers to the UI-generator of a given component. This is analagous to the UI function of a given shiny module.
The component template function must be of the following structure (note the ns argument):
function(ns) { # must either return an object that can # be coerced to a `shiny::tags` object or a `shiny::tagList` }
The following is an example of a valid template:
function(ns) { shiny::div("Hello world!") }
The template function requires one argument: ns
.
ns
is used identically to shiny modules, and
helps distinguish between component instances.
Component templates can be nested through the use of slots. Slots are placeholder elements that tell rsx where tags should be placed.
x <- component( name = "slots", template = function(ns) { shiny::tagList( shiny::tags$slot(), shiny::p("bar") ) } ) print(x(shiny::p("foo"))) #> <rsx::instance> `slots` #> <p>foo</p> #> <p>bar</p>
You can also specify if you'd like content to be used in the case that a slot isn't filled – this is typically called "fallback" content.
x <- component( name = "fallback", template = function(ns) { shiny::tags$slot( shiny::p("Hello!") ) } ) print(x()) #> <rsx::instance> `fallback` #> <p>Hello!</p>
Slots can be further distinguished by name attributes, which can be used to target specific areas of the template.
x <- component( name = "named_slots", template = function(ns) { shiny::tagList( shiny::tags$slot(name = "a"), shiny::tags$slot(name = "b") ) } ) print(x(shiny::p("bar", slot = "b"), shiny::p("foo", slot = "a"))) #> <rsx::instance> `named_slots` #> <p>foo</p> #> <p>bar</p>
Other components:
component-data
,
component-methods
,
component-styles
,
component()
Given a component instance tag x
, decompose the instance
into separate server and UI elements.
decompose(x)
decompose(x)
x |
a shiny tag returned from calling a component |
list
comp <- component( name = "decompose", template = function(ns) { shiny::div("hello world") }, methods = list( setup = function(input, output, session) { # noop } ) ) x <- decompose(comp()) print(x)
comp <- component( name = "decompose", template = function(ns) { shiny::div("hello world") }, methods = list( setup = function(input, output, session) { # noop } ) ) x <- decompose(comp()) print(x)
Is an instance tag
is.instance_tag(x)
is.instance_tag(x)
x |
object |
Create a new instance of an rsx application by passing a top level
rsx::component as the application root. This is analagous to running shiny::shinyApp()
.
rsx_app(root, ..., resource_path = NULL, app_class = "App")
rsx_app(root, ..., resource_path = NULL, app_class = "App")
root |
an |
... |
further arguments passed to |
resource_path |
path to a resource folder, if |
app_class |
the html class attribute for the app wrapper |
Other compilation:
rsx_server()
,
rsx_ui()
rsx_module_server
is a low-level function that loads up the module servers of all instantiated components.
This is similar to rsx_server
, but also allows for namespacing
the rsx server.
rsx_module_server(id)
rsx_module_server(id)
id |
unique namespace |
rsx_server
is a low-level function that loads up the module servers of all instantiated rsx::components.
If the rsx_app
function is not viable for your shiny application setup, the rsx_server function
can be used.
rsx_server()
rsx_server()
Some things to note:
Due to randomised hashing of component namepaces, shiny modules are not nested in rsx
If a component does not have a setup function, a module server will not be created for the instance
shiny server function
Other compilation:
rsx_app()
,
rsx_ui()
rsx_ui
is a low-level function for creating rsx ui without the use of rsx_app
.
rsx_ui(root, id = NULL, app_class = "App", resource_path = NULL)
rsx_ui(root, id = NULL, app_class = "App", resource_path = NULL)
root |
a component used as the top-level node for the shiny app |
id |
TODO |
app_class |
the html class attribute for the app wrapper |
resource_path |
path to a resource folder, if |
shiny::tagList
object
Other compilation:
rsx_app()
,
rsx_server()