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)
Related
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>"))
})
})
}
I want create output spaces in UI part of Shiny conditionally. If the variable in server changed, the output spaces should be defined in UI.
library(shiny)
ui <- fluidPage(
mainPanel("main panel", textOutput("ts_txt_main"))
)
server <- function(input, output) {
observe({
for (i in 1:10) {
output[[paste0("ts_txt",i)]<- renderText({ "Some_text" })
}
})
}
shinyApp(ui = ui, server = server)
I need textOutput("ts_txt_main") to be changed or extended to textOutput("ts_txt1"), textOutput("ts_txt2"), textOutput("ts_txt3"), textOutput("ts_txt4"), textOutput("ts_txt5"), textOutput("ts_txt6"), textOutput("ts_txt7"), textOutput("ts_txt8"), textOutput("ts_txt9"), textOutput("ts_txt10")
I am baffled why the following code produces the "Shiny.setInputValue is not a function" error:
library(shiny)
library(htmltools)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Test"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
),
# Show a plot of the generated distribution
mainPanel(
shiny::tags$script(htmltools::HTML('
quantityaa = 1;
console.log(quantityaa);
Shiny.setInputValue("hi", quantityaa);
'))
,
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
hi <- reactive({ input$hi})
print(hi)
}
# Run the application
shinyApp(ui = ui, server = server)
What is wrong with this code that produces this error? I cannot see anything wrong with it, but I must be missing something.
That's because Shiny is not ready yet. Use the shiny:connected event:
$(document).on("shiny:connected", function() {
// your awesome JavaScript here can use Shiny.setInputValue
});
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")
I am trying to include an image in a shiny app. I want: "if it is type 1 plot "this image", if it is type 0 plot "this other image". I know that I have to put the jpg files into the folder where the app.R is and then call it but I do not know how.
This is the code I have used until now (it works), I just have to include the images in the render.
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Myapp"),
#Inputs
dateInput(inputId = "dob", label="Birth"),
dateInput(inputId = "ad", label="Date"),
actionButton("Submit", icon("fas fa-magic"), label="Submit"),
#Outputs
textOutput(outputId = "textR"),
imageOutput(outputId = "imageR1"),
imageOutput(outputId="imageR2")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#my output should be named textR and imageR1, imageR2
observeEvent(input$Submit,
output$textR<-renderText({
v1<-as.numeric(as.Date(input$ad,format="%Y/%m/%d") - as.Date(input$dob, format="%Y/%m/%d"))/30.5
value_v1<-ifelse(v1>48, "type1", "type2")
print(value_v1)
}))
}
# Run the application
shinyApp(ui = ui, server = server)
It is bad practice to define an output object inside an observeEvent. In this case, independent on the choice of how to switch the images, I would advice to use an eventReactive - let's call that myval. That creates a reactive which only changes when a certain event happens, in this case a click on the submit button. We can then refer to this in the body of the renderText statement, so that can simply become:
output$textR<-renderText({
print(myval())
})
Secondly, for the outputting of images, you should place those in the www directory, see here. We can then create an ui element with renderUI and UIOutput, in which we use the value of our eventReactive myval() to select the image to display.
A working example is given below. Note that I saved it as app.R, and used the folder structure of the referred link, so:
| shinyApp/
| app.R
| www/
| zorro.jpg
| notzorro.jpg
Hope this helps!
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Myapp"),
#Inputs
dateInput(inputId = "dob", label="Birth"),
dateInput(inputId = "ad", label="Date"),
actionButton("Submit", icon("fas fa-magic"), label="Submit"),
#Outputs
textOutput(outputId = "textR"),
uiOutput(outputId = "my_ui")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
myval <- eventReactive(input$Submit,
{
v1<-as.numeric(as.Date(input$ad,format="%Y/%m/%d") - as.Date(input$dob, format="%Y/%m/%d"))/30.5
return(ifelse(v1>48, "type1", "type2"))
})
output$textR<-renderText({
print(myval())
})
output$my_ui<-renderUI({
if(myval()=='type1')
img(src='zorro.jpg', height = '300px')
else
img(src='notzorro.jpg', height = '300px')
})
}
# Run the application
shinyApp(ui = ui, server = server)