I have a code that allows to dynamically add modules in a Shiny app. This module is composed of a selectInput and can be added by clicking on the "Add filter" Button.
What I try to do is to put text at the right of each selectInput widget which value update when the user click on the perform Button and is equal to the selection on the selectInput
I don't know how to do. Many tries were unsuccessfull...
The code is the following :
library(shiny)
moduleFilterUI <- function(id) {
ns <- NS(id)
uiOutput(ns("SymbolicFilter"))
}
moduleSymbolicFilter <- function(input, output, session) {
output$SymbolicFilter <- renderUI({
fluidRow(
column(width = 4, selectInput(session$ns("cname"), "Column name", choices = c(1:5)))
)
})
}
ui <- fluidPage(
fluidRow(
actionButton("addSymbolicFilterModule", "Add filter"),
actionButton("Filter", "Perform"),
uiOutput("symbolicFilters"))
)
)
server <- function(input, output, session) {
symbolicFilterModules <- list()
makeReactiveBinding("symbolicFilterModules")
observeEvent(input$addSymbolicFilterModule, {
duplicateSymbolicFilterid <- paste0("duplicateSymbolicFilter", input$addSymbolicFilterModule)
symbolicFilterModules <<- c(symbolicFilterModules, list(moduleSymbolicFilterUI(duplicateSymbolicFilterid)))
callModule(moduleSymbolicFilter, duplicateSymbolicFilterid)
shinyjs::disable("addSymbolicFilterModule")
iLast <- length(symbolicFilterModules)
for (i in 1:(iLast-1)){
duplicateSymbolicFilterid <- paste0("duplicateSymbolicFilter", i)
updateSelectInput(session, paste0(duplicateSymbolicFilterid,"-cname"),
selected=input[[paste0(duplicateSymbolicFilterid,"-cname")]])
}
})
observeEvent(input$Filter,{
shinyjs::enable("addSymbolicFilterModule")
iLast <- length(symbolicFilterModules)
duplicateSymbolicFilterid <- paste0("duplicateSymbolicFilter", iLast)
cname <- input[[paste0(duplicateSymbolicFilterid,"-cname")]]
for (i in 1:(iLast)){
duplicateSymbolicFilterid <- paste0("duplicateSymbolicFilter", i)
updateSelectInput(session, paste0(duplicateSymbolicFilterid,"-cname"),
selected=input[[paste0(duplicateSymbolicFilterid,"-cname")]])
}
})
output$symbolicFilters <- renderUI({
symbolicFilterModules
})
}
shinyApp(ui = ui, server = server)
maybe you had already solved the problem, but...
you named the module moduleFilterUI, but you call moduleSymbolicFilterUI...
Related
I created an application to auto-complete postal addresses. The application works but the problem is that the list of choices only updates when you click to select the text. Would it be possible to update the list of choices automatically by typing the text?
Here is the code :
library(devtools)
install_github("AnalytixWare/ShinySky")
library(shinysky)
library(shiny)
library(httr)
library(jsonlite)
library(shinyjs)
GetAddress<-function(input=""){
if(input==""){
return("")
}else{
query<-paste("https://api-adresse.data.gouv.fr/search/?q=", URLencode(input),sep="")
res<-GET(query)
data<-fromJSON(rawToChar(res$content))
return(data$features$properties$label)
}
} #F
ui <- fluidPage(
select2Input("txt","Saisir votre adresse",choices = NULL,multiple=TRUE),
verbatimTextOutput("selected_txt"),
)
server <- function(input, output,session) {
# Create object for reactive values
rv <- reactiveValues(
value_store = character()
)
# When input changes -> update
observeEvent(input$txt, {
output$selected_txt <- renderText({
paste(input$txt)
})
rv$value_store <- input$txt
output$test <- renderText({
paste(rv$value_store)
})
pubs <- GetAddress(rv$value_store)
updateSelect2Input(session, 'txt', choices =pubs,label="")
})
}
shinyApp(ui = ui, server = server)
In my shiny-app I use pop-up boxes to allow the user to quickly look up data in a table. That works well using showModal, however currently I'm creating some module servers and there I run into issues. What I understood, is that the id in a module ui needs to be defined using ns (as is done below for the action button).
However, my data table inside the pop-up box is defined inside the moduleServer. If I use ns("my_database") there, I get the message that ns is not a recognized function.
How can I get that table to show inside my pop-up box, embedded in a moduleServer?
library(shiny)
dataPage <- function(id, label = "Counter") {
ns <- NS(id)
tagList(
column(12,
h1("Testing my database pop up"),
actionButton(ns("show_database"), "Show my database")
)
)
}
dataServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
observeEvent(input$show_database, { # Pop-up box, currently for testing purposes
showModal(modalDialog(
title = "My database",
"My database",
dataTableOutput("my_database"),
easyClose = TRUE,
footer = NULL
))
})
output$my_database <- renderDataTable({
temp <- data.frame(a = seq(1,10), b = letters[1:10])
temp
})
}
)
}
ui <- fluidPage(
dataPage("data1", "Counter #1")
)
server <- function(input, output, session) {
dataServer("data1")
}
shinyApp(ui, server)
What I want to achieve is to get access to the reactive value passed to a parent module from a child module. The reproducible example below shows the idea. When i click the button in mod_server_btn then its value should be printed out in the console (from within parent module):
library(shiny)
mod_ui_btn <- function(id, label = "ui1UI") {
ns <- NS(id)
shinyUI(fluidPage(
actionButton(ns("confirm"), "Submit", class='btn-primary')
))
}
mod_server_btn <- function(input, output, session) {
cond <- reactive({ input$confirm})
return(cond)
}
ui =fluidPage(
mod_ui_btn("test"),
uiOutput("example")
)
server=shinyServer(function(input, output, session) {
value <- callModule(mod_server_btn,"test")
print(value)
#print(value$cond) # these 3 don't work either
#print(value()$cond)
#print(value())
})
shinyApp(ui=ui,server=server)
However, it doesn't work. When I click the button then I got a text: reactive({input$confirm}) in the console and it's not what I want, I need to access button value. General question is - is it possible at all to get access to reactive value in a parent module?
EDIT: #rbasa, #YBS thanks for your answers. In fact in my real app I need to return more than one reactive value to parent module. Below is slightly changed code - I added second button in mod_ui_btn - now I need to return values from both buttons to the server module. I made a list of reactives but can't get access to them using observe or output$example <-:
library(shiny)
mod_ui_btn <- function(id, label = "ui1UI") {
ns <- NS(id)
shinyUI(fluidPage(
actionButton(ns("confirm"), "Submit", class='btn-primary'),
actionButton(ns("confirm2"), "Submit2", class='btn-primary')
))
}
mod_server_btn <- function(input, output, session) {
return(
list(
cond = reactive({ input$confirm}),
cond2 = reactive({ input$confirm2})
)
)
}
ui =fluidPage(
mod_ui_btn("test"),
verbatimTextOutput("example"),
verbatimTextOutput("example2")
)
server=shinyServer(function(input, output, session) {
value <- callModule(mod_server_btn,"test")
output$example <- renderPrint(value$cond)
output$example2 <- renderPrint(value$cond2)
observe({
print(value$cond) #this is how I usually catch reactives - by their name
print(value$cond2)
})
})
shinyApp(ui=ui,server=server)
I usually use return(list(..some reactive values)) to return more than one ractive value to other module and catch then using their names in parent module. Here it doesn't work even if I use observe. No value is returned.
You can access with value(). I would recommend to change your mod_server_btn to the one shown below, and notice the call in server. EDIT: updated for multiple variables. Try this
library(shiny)
mod_ui_btn <- function(id, label = "ui1UI") {
ns <- NS(id)
shinyUI(fluidPage(
actionButton(ns("confirm"), "Submit", class='btn-primary'),
actionButton(ns("confirm2"), "Submit2", class='btn-primary')
))
}
mod_server_btn <- function(id) {
moduleServer(id, function(input, output, session) {
return(
list(
cond = reactive(input$confirm),
cond2 = reactive(input$confirm2)
)
)
})
}
ui =fluidPage(
mod_ui_btn("test"),
verbatimTextOutput("example"),
verbatimTextOutput("example2")
)
server=shinyServer(function(input, output, session) {
# value <- callModule(mod_server_btn,"test")
value <- mod_server_btn("test")
output$example <- renderPrint(value$cond())
output$example2 <- renderPrint(value$cond2())
observe({
print(value$cond()) #this is how I usually catch reactives - by their name
print(value$cond2())
})
})
shinyApp(ui=ui,server=server)
I’m looking for some help with a simple Shiny app with a modularised design please. I think the problem is a name space issue so the example below is set out as a simplified version of my actual project.
The aim is for ‘tab_3’ on the tabsetPanel to only show when the ‘View Tab_3’ is checked, which works fine. I would like to update the tabsetPanel however to also select ‘tab_3’ when ‘View Tab_3’ is checked and this is not firing as desired.
I can get the tabsetPanel to also select ‘tab_3’ when ‘View Tab_3’ is checked if I wrap the tabsetPanel’s id in a namespace function, id = ns("tab_a_tha"), however then I lose the show/hide functionality of ‘tab_3’.
My hunch is that the solution lies within providing a namespace to the toggle function but I haven’t found any clues on how to approach it.
library(shiny)
library(shinyjs)
inner_moduleUI <- function(id){
ns <- NS(id)
tagList(
fluidRow(checkboxInput(ns("chckbx"), "View Tab_3", value = F)),
tabsetPanel(
id = "tab_a_tha",
# id = ns("tab_a_tha"),
tabPanel('tab_1'),
tabPanel('tab_2'),
tabPanel('tab_3')
)
)
}
inner_module <- function(input, output, session){
observeEvent(input$chckbx, {
toggle(condition = input$chckbx, selector = "#tab_a_tha li a[data-value=tab_3]")
if(input$chckbx == T){
updateTabsetPanel(session, 'tab_a_tha', selected = 'tab_3')
}
})
}
ui <- fluidPage(
useShinyjs(),
uiOutput('main_ui')
)
server <- function(input, output, session) {
output$main_ui <- renderUI({inner_moduleUI('inner_ns') })
callModule(inner_module, 'inner_ns')
}
shinyApp(ui = ui, server = server)
you are right the problem is with the namespace. The trick is that you can access the namespace function also in the server part of a module with session$ns.
Using this and wrapping the tap id in the ns function. We can use paste0 to generate the new selector of the toggle function. We get something like this:
library(shiny)
library(shinyjs)
inner_moduleUI <- function(id){
ns <- NS(id)
tagList(
fluidRow(checkboxInput(ns("chckbx"), "View Tab_3", value = F)),
tabsetPanel(
id = ns("tab_a_tha"),
# id = ns("tab_a_tha"),
tabPanel('tab_1'),
tabPanel('tab_2'),
tabPanel('tab_3')
)
)
}
inner_module <- function(input, output, session){
observeEvent(input$chckbx, {
toggle(condition = input$chckbx, selector = paste0("#",session$ns("tab_a_tha")," li a[data-value=tab_3]"))
if(input$chckbx == T){
updateTabsetPanel(session, 'tab_a_tha', selected = 'tab_3')
}
})
}
ui <- fluidPage(
useShinyjs(),
uiOutput('main_ui')
)
server <- function(input, output, session) {
output$main_ui <- renderUI({inner_moduleUI('inner_ns') })
callModule(inner_module, 'inner_ns')
}
shinyApp(ui = ui, server = server)
I am using renderUI to optionally present a Table or Plot based on user selection of the visualization option. I am also using Shiny modules to present the same thing on multiple tabs. While I have gotten Shiny modules to work wonderfully in another app, I am struggling to get it to work with renderUI.
Here is a minimal piece of code that I came up with that shows the problem where nothing gets displayed on either tabs:
myUI <- function(id) {
ns <- NS(id)
fluidRow(
uiOutput(ns('myFinalText'))
)
}
ui <- fluidPage(
tabBox(id = 'myBox', width = 12,
tabPanel('Tab1',
fluidRow(
myUI('tab1')
)),
tabPanel('Tab2',
fluidRow(
myUI('tab2')
))
)
)
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput('myText')
})
}
server <- function(input, output, session) {
callModule(myTextFunc, 'tab1', session = session, 'Hello Tab1')
callModule(myTextFunc, 'tab2', session = session, 'Hello Tab2')
}
shinyApp(ui = ui, server = server)
Any thoughts on what else I should be doing to make this work?
Replacing the Shiny module UI function and server functions as follows makes it work fine.
myUI <- function(id) {
ns <- NS(id)
fluidRow(
textOutput(ns('myFinalText'))
)
}
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderText({
text
})
}
You can get the namespace from the session object. Change myTextFunc in the initial app like this:
myTextFunc <- function(input, output, session, text) {
ns <- session$ns
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput(ns('myText'))
})
}
You shouldn't call output$ function from another output$ function - it's against Shiny design patterns.
output$myFinalText <- renderUI({
output$myText <- renderText({text})
textOutput(ns('myText'))
})
If you want to know, why it is very bad practice, watch Joe Cheng tutorial about 'Effective reactive programming' from this site: https://www.rstudio.com/resources/webinars/shiny-developer-conference/.
You should use rather reactiveValues or reactive expressions instead. What exactly you should use is dependent from what do you want to achieve, so it's hard to say without detailed example, but according to Joe Cheng everything can be accomplished without nesting outputs or observers.
Sorry for answering my own question...but for others looking for a similar solution, this may be of help.
Here is how I solved for the need to inherit Shiny module namespace on the server side to dynamically render UI. IF there is a better way to solve, please comment or post.
tab1NS <- NS('tab1')
tab2NS <- NS('tab2')
myUI <- function(ns) {
tagList(
fluidRow(
radioButtons(ns('type'), 'Select Visual:',
choices = c('Table' = 'table',
'Plot' = 'plot'))
),
fluidRow(
uiOutput(ns('myCars'))
)
)
}
ui <- fluidPage(
tabBox(id = 'myBox', width = 12,
tabPanel('Tab1',
fluidRow(
myUI(tab1NS)
)),
tabPanel('Tab2',
fluidRow(
myUI(tab2NS)
))
)
)
myTextFunc <- function(input, output, session, cars, ns) {
getMyCars <- reactive({
if (input$type == 'table') {
output$table <- renderDataTable({datatable(cars)})
dataTableOutput(ns('table'))
} else{
output$plot <- renderPlot({
plot(cars$wt, cars$mpg)
})
plotOutput(ns('plot'))
}
})
output$myCars <- renderUI({
getMyCars()
})
}
server <- function(input, output, session) {
callModule(myTextFunc, 'tab1', session = session,
mtcars[mtcars$am == 1, ], tab1NS)
callModule(myTextFunc, 'tab2', session = session,
mtcars[mtcars$am == 0, ], tab2NS)
}
shinyApp(ui = ui, server = server)
Replacing your functions with this renderUI equivalent also works:
myUI <- function(id) {
ns <- NS(id)
fluidRow(
uiOutput(ns('myFinalText'))
)
}
myTextFunc <- function(input, output, session, text) {
output$myFinalText <- renderUI({
text
})
}
Although this obviously does not capture the complexity of what you are really doing. There's something not right about using output$... and textOutput within the renderUI like that. I don't think that is necessary - you don't actually have to use the textOutput function to include text in your output.
EDIT: It occurs to me that the problem has to do with namespaces and modules. When you do output$myText <- renderText(text), the result ends up in the namespace of tab1 or tab2. For example, try changing your textOutput to
textOutput('tab1-myText')
and watch what happens. I think this is why having output$.. variables in your renderUI is problematic. You can access inputs via callModule and that should take care of any namespace issues.