I have a Shiny app that I am trying to "modularize". I have an issue that my subtab tabPanel tab_Summary is not recognized when I separate it in another R file.
If I place the creation of the tab_Summary inside the ui.R it works, but if I want to be able to have this subtab in another file like showed in the following scripts, then I get error that object 'tab_Summary' not found :
The 0_tab_Summary_ui.R placed in the folder 'C:/Users/ROG/Downloads/example_shiny/Shiny_Modules':
tab_Summary <- tabPanel('Summary',
fluidRow(
column(width = 3,
htmlOutput("Summary_Number_ui")
)
)
)
The ui.R script:
setwd(paste0(main_working_dir, "Shiny_Modules"))
source("0_tab_Summary_ui.R")
ui <- navbarPage(
title=div("SHINY DASHBOARD"),
tab_Summary
)
The server.R script:
server <- function(input, output, session) {
output$Summary_Number_ui <- renderUI({
HTML(paste0("<div id='mydiv'><font size='5'><font color=\"#0d0a36\"> Total Number of Accounts: <b>", 726431 , "</b></div>"))
})
}
The app.R script:
library(shiny)
local_working_dir <- "C:/Users/ROG/Downloads/example_shiny/"
main_working_dir <- local_working_dir
setwd(main_working_dir)
shinyApp(ui, server)
And below the ui.R script that does not show any error but is not modularized:
setwd(paste0(main_working_dir, "Shiny_Modules"))
source("0_tab_Summary_ui.R")
ui <- navbarPage(
title=div("SHINY DASHBOARD"),
# tab_Summary
tab_Summary <- tabPanel('Summary',
fluidRow(
column(width = 3,
htmlOutput("Summary_Number_ui")
)
)
)
)
Try to learn how to use modules in Shiny, roughly global.R "includes" all your modules and those files have a module specific UI and a Server part that belong together. In the ui.R you define your layout and call the specific module UI part, same for the server.R. This way you keep all code for one module together, which makes it nicely scalable. Also note that whatever settings you may want to use and define, global.R is excecuted once upon the start of your app, while all code within your server.R server function is run upon every browser refresh.
global.R
# Global.R is loaded once at App start (not at browser refresh!)
# load all libraries
library("shiny")
# source all your modules here
source("modules/MyTabModule.R")
ui.R
ui <- navbarPage(
title=div("SHINY DASHBOARD"),
MyTabModuleUI("Summary_Number_ui")
)
server.R
server <- function(input, output, session) {
MyTabModuleServer("Summary_Number_ui")
}
modules/MyTabModule.R
MyTabModuleUI <- function(id) {
ns <- NS(id)
tabPanel('Summary',
fluidRow(
column(
width = 3,
htmlOutput(ns("Summary_Number_ui"))
)
)
)
}
MyTabModuleServer <- function(id) {
moduleServer(id, function(input, output, session) {
output$Summary_Number_ui <- renderUI({
HTML(paste0("<div id='mydiv'><font size='5'><font color=\"#0d0a36\"> Total Number of Accounts: <b>", 726431 , "</b></div>"))
})
})
}
Related
super new to shiny, have a problem that seems like it should be basic reactive programming but I haven't been able to find a solution that's worked so far.
Essentially, I want to take the user's selected input from the UI and paste it into a simple object in the server that will react/update when a new input is chosen.
The object will be concatenated into a full API call, and I wish to rerun the API call in the server with the reactive object updated each time a new input is chosen for it (note: the API cannot be run without an access code which is part of a corporate account, so apologies for my hesitance to put my full code but I just need help with this one functionality.)
In code below:
with Dollar General as the default selection in the selectInput, I would like the object, query, to be the character string "dollar%20general", and reactively change to "walmart" should Walmart be selected
Thanks!
ui <- fluidPage
sidebarLayout(
sidebarPanel(
selectInput("company", "Choose company:",
c("Dollar General" = "dollar%20general",
"Dollar Tree" = "dollar%20tree",
"Walmart" = "walmart"))
...
server <- function(input,output) {
...
query <- paste(input$company)
...
you can use reactiveValues() and observe. This should work:
library(shiny)
# Define UI for application
ui <- fluidPage(
# your input
sidebarLayout(
sidebarPanel(
selectInput("company", "Choose company:",
c("Dollar General" = "dollar%20general",
"Dollar Tree" = "dollar%20tree",
"Walmart" = "walmart"))
),
# Determine Output
mainPanel(
textOutput("showInput") # you need to render this in your server file
)
)
)
server <- function(input, output) {
# Show what was selected
query <- reactiveValues()
observe(
query$test <- paste(input$company, "and test", sep = " ")
)
output$showInput <- renderText({ #based on what you defined in the ui
query$test
})
}
# Run the application
shinyApp(ui = ui, server = server)
Create two files named ui.R and server.R store the UI logic in ui.R and backend/object logic in server.R. Below is the implementation.
UI file
# UI of app
ui <- fluidPage(
# input
sidebarLayout(
sidebarPanel(
selectInput("company", "Choose company:",
c("Dollar General" = "dollar%20general",
"Dollar Tree" = "dollar%20tree",
"Walmart" = "walmart"))
),
# Output
mainPanel(
textOutput("Input")
)
)
)
Server/Backend File
server <- function(input, output) {
# Show what was selected
output$Input <- renderText({ #based on what you defined in the ui
input$company
})
}
Now store these in a directory and then call runApp function.
~/newdir
|-- ui.R
|-- server.R
runApp("newdir")
Is it possible to create modules in a Shiny App which is written down in separated app.R, server.R and ui.R files?
All the examples I found are one app.R file with embedded server and ui functions in. See example 1, example 2!, example 3!
(I took the code from this last example for this test).
I tried to run this app:
app.R
library(shiny)
# load module functions
source("hello_world.R")
# Run the application
shinyApp(ui = ui, server = server)
ui.R
#ui.R
library(shiny)
#source("hello_world.R")
ui <- fluidPage(
titlePanel("Using of Shiny modules"),
fluidRow(
# Call interface function of module "hello_world"
hello_worldUI(id = "id_1")
)
)
server.R
#server.R
library(shiny)
source("hello_world.R")
server <- function(input, output, session) {
# Call logic server function of module "hello_world"
callModule(module = hello_world, id = "id_1")
}
# UPDATE! -> my Error comes from this line of code in server.R file:
#shinyApp(ui = ui, server = server)
#Removing the line above solve the problem.
hello_world.R
#module 1: hello_world
# Function for module UI
hello_worldUI <- function(id) {
ns <- NS(id)
fluidPage(
fluidRow(
column(2, textInput(ns("TI_username"), label = NULL, placeholder = "your name")),
column(2, actionButton(ns("AB_hello"), label = "Hello !"))
),
hr(),
fluidRow(
column(12, textOutput(ns("TO_Hello_user")))
)
)
}
# Function for module server logic
hello_world <- function(input, output, session) {
# When user clicks on "Hello" button : Update reactive variable "name"
name <- eventReactive(input$AB_hello, {
return(input$TI_username)
})
# Show greetings
output$TO_Hello_user <- renderText({
if (name() %in% "") {
return("Hello world !")
} else {
return(paste("Hello", name(), "!"))
}
})
}
But I got this error:
Warning: Error in force: object 'ui' not found
52: force
51: uiHttpHandler
50: shinyApp
Error in force(ui) : object 'ui' not found
The ui and server objects are not known to the app unless you define them in the same file and they are generated at run time, or you explicitly call them from outside files before shinyApp(). Change your app.R like below, and it should work:
library(shiny)
# load module functions
source("hw.R")
# load ui elements
source("ui.R")
# load server function
source("serv.R")
# Run the application
shinyApp(ui = ui, server = server)
I'm trying to make a dynamically generated navbar based on the session user id.
I have a data table that maps the session user to a list of that user's clients. I want the app to produce a navbar where each tabPanel is for each client that user has. I'm not sure how I can easily do that since navbarPage() doesn't take a list argument.
Below is my example
library(shiny)
data <- data.frame(user=c("emily", "emily"), clients=c("client1", "client2"))
CreateCustomNavbarContent <- function(data) {
l <- lapply(data$clients, function(client) {
tabPanel(client,
h2(client))
})
renderUI({
l
})
}
shinyApp(
ui <- fluidPage(
uiOutput("custom_navbar")
),
server <- function(input, output) {
output$custom_navbar <- renderUI({
## commented below doesn't work
# navbarPage(
# CreateCustomNavbarContent(data)
# )
navbarPage("",
tabPanel("client1",
h2("client1")
),
tabPanel("client2",
h2("client2")
)
)
})
}
)
You could achieve what you want with do.call, so we can pass a list of arguments as separate arguments. Below is a working example, I gave emily a companion called John so you can validate that the code does what you want ;)
Hope this helps!
library(shiny)
data <- data.frame(user=c("Emily", "Emily","John","John"), clients=c("client1", "client2","client3","client4"))
ui = fluidPage(
selectInput('select_user','Select user:',unique(data$user)),
uiOutput('mytabsetpanel')
)
server = function(input, output, session){
output$mytabsetpanel = renderUI({
myTabs = lapply(data$clients[data$user==input$select_user], tabPanel)
do.call(tabsetPanel, myTabs)
})
}
shinyApp(ui,server)
I am using shiny modules with some charts and everything works fine (amazing functionality!), ... but I can not make them work with valueBox (from shinydashboard). Nothing is rendered...
Here is a minimal example:
library(shinydashboard)
# MODULE UI
bsc_tile_UI <- function(id) {
ns <- NS(id)
valueBoxOutput("tile1", width=12)
}
# MODULE Server
bsc_tile_OUT <- function(input, output, session, number, metric) {
output$tile1 <- renderValueBox({
valueBox(number, paste(metric), icon = icon("arrow-up"),color = "blue",
width=12)
})
}
ui<-dashboardPage(
dashboardHeader(title = "Dashboard"),
sidebar <- dashboardSidebar(disable = TRUE),
dashboardBody(
fluidPage(
bsc_tile_UI("tile_1"),
bsc_tile_UI("tile_2")
)
)
)
# App server
server <- function(input, output,session){
callModule(bsc_tile_OUT, "tile_1", '300', 'metric 1')
callModule(bsc_tile_OUT, "tile_2", '500', 'metric 2')
}
shinyApp(ui, server)
In the given example parameteres "number" and "metric" are explicitly provided, but my intention is they will be defined as variables of a dataframe.
Any help will be welcome!!!
(sorry my english)
You need to use ns() in all your defined Module inputs. The module server is taking the members of the output vector and will internally attach the module ID to them ("tile_1" and "tile_2" in your case) - this is what you need to do manually in UI by using ns(). So if you just change your module UI output definition to the following, your code will work:
valueBoxOutput(ns("tile1"), width=12)
Here is my code in R Shiny using modules.
I created a module named MyModule and want to generate two UI elements: selectInput and textInput. This code is just an example - in my real application second element require the result from the first element, so I want to generate them separately.
I don't understand why the second uiOutput doesn't generate the UI element it indended to:
library(shiny)
# Define UI
ui <- shinyUI(fluidPage(MyModuleUI("one")))
# Define server logic
server <- shinyServer(function(input, output, session) {callModule(MyModule, 'one')})
#Here is my UI Module
MyModuleUI <- function(id) {
ns <- NS(id)
fluidRow(
uiOutput(ns('ChooseNumber')),
uiOutput(ns('EnterText'))
)
}
#Here is my server Module
MyModule <- function(input, output, session) {
output$ChooseNumber <- renderUI({
# In my bigger program I need this UI to be generated with some database values,
# thats why it is in the Server part of the Module
ns <- session$ns
selectInput(ns("TheNumber"), label = 'Select a number', c(1,2,3))
})
# Same here
output$EnterText <- renderUI({
ns <- session$ns
textInput(ns('TheText'),label = 'Enter a text:',value = 'ABC')
})
}
# Run the application
shinyApp(ui = ui, server = server)
Thank you!