Printing output based on dynamic user input in R Shiny - r

I am quite new to R shiny and so have not been able to figure out the solution from similar questions posted on this site. I am trying to read and use the input that a user provides to R shiny to generate an output.
I am trying to create a simple GUI where a user selects the name of a person (from a drop-down menu) and then enters his/her weight. If the height is above a certain threshold the output recommendation is "Gain Weight", else it is "Loose Weight".
Everything seems to be working fine, except for the following error from the Server.R file:
Error in `$.shinyoutput`(output, value_weight) :
Reading objects from shinyoutput object not allowed
How can I read and use the variable 'value_weight' in an if-then-else condition?
Main.R
library(shiny)
runApp()
Server.R
function(input, output) {
# You can access the value of the widget with input$select, e.g.
output$value_name <- renderPrint({ input$select })
output$value_weight <- renderPrint({ input$num })
if(output$value_weight > 150)
{
output$value_recommendation <- "Loose Weight"
}
else{
output$value_recommendation <- "Gain Weight"
}
}
UI.R
names_list <- list("Adam", "Jenna","Peter")
fluidPage(
selectInput("select", label = h3("Select Name"), choices = names_list, selected = 1),
hr(),
fluidRow(column(3, verbatimTextOutput("value_name"))),
numericInput("num", label = h3("Enter Weight"), value = 0),
hr(),
fluidRow(column(3, verbatimTextOutput("value_weight"))),
hr(),
fluidRow(column(3, verbatimTextOutput("value_recommendation")))
)

The problem in your code is the line
if(output$value_weight > 150)
Generally speaking, outputs are write-only objects in the server, while inputs are readonly. If you replace output$value_weight with input$num, everything should work fine. You also need to use a render-function for outputs: in this case renderPrint or renderText (see the documentation for the difference between those two render functions).
## server.R
function(input, output) {
# You can access the value of the widget with input$select, e.g.
output$value_name <- renderPrint({ input$select })
output$value_weight <- renderPrint({ input$num })
output$value_recommendation <- renderPrint({
if(input$num > 150)
"Loose Weight"
else
"Gain weight"
})
}
Another way to do this is using a call to the function reactive
## server.R
function(input, output) {
# You can access the value of the widget with input$select, e.g.
output$value_name <- renderPrint({ input$select })
value_weight <- reactive({ input$num })
output$value_weight <- renderPrint({ value_weight() })
output$value_recommendation <- renderPrint({
if(value_weight() > 150)
"Loose Weight"
else
"Gain weight"
})
}

Using 'renderText' solved the issue!
Server.R
function(input, output)
{
output$value_market <- renderPrint({ input$select })
output$value_demand <- renderPrint({ input$num })
output$value_recommendation <- renderText({
if(input$num > 150)
{
print("Loose Weight")
}
else{
print("Gain Weight")
}
})
}

Related

How to save input to data frame, and use it later in Shiny?

I want to save the value from username input if it doesn't exist in data frame, and render text if it already exists (for reprex purpose).
Rendering text part works perfectly, but I don't know how to save it and use it later.
I want to save the value permanently, not only on current session
I've got this error:
Warning: Error in <-: invalid (NULL) left side of assignment
library(shiny)
ui <- fluidPage(
textInput("username", "username"),
actionButton("save", "Save!"),
textOutput("confirmation")
)
server <- function(input, output, session) {
df <- reactive(data.frame(column1 = "user1"))
exist <- reactive(input$username %in% df()$column1)
observeEvent(input$save, {
if (exist() == TRUE) {
output$confirmation <- renderText("Username already exists!")
} else {
df() <- rbind(df(), input$username) # <-- THIS dosn't work
}
})
}
shinyApp(ui, server)
EDIT:
Thanks to #I_O answer, I figured out this solution
reactiveVal() keep the changes in current session.
write_csv() and read_csv() part, allows app to use previously saved usernames.
saved_df <- read_csv("C:\\Users\\Przemo\\Documents\\R\\leaRn\\Shiny\\Moodtracker\\testers\\test_safe.csv")
ui <- fluidPage(
textInput("username", "username"),
actionButton("save", "Save!"),
textOutput("confirmation")
)
server <- function(input, output, session) {
df <- reactiveVal(saved_df)
exist <- reactive(input$username %in% df()$column1)
observeEvent(input$save, {
if (exist() == TRUE) {
output$confirmation <- renderText("Username already exists!")
} else {
output$confirmation <- renderText("")
df(rbind(df(), input$username))
write_csv(df(), "C:\\Users\\Przemo\\Documents\\R\\leaRn\\Shiny\\Moodtracker\\testers\\test_safe.csv")
}
})
}
shinyApp(ui, server)

Click Interactive Plot in R Shiny

I'm trying to create a plot with a bunch of boxes and then when a box gets clicked on it gets colored in up. I'm having two issues with this. 1. I can't figure out a way for the figure to update dynamically when I click. 2. I can't figure out how to store the values that come out of the click input variable so that I have stored all previous clicks and would be able to color in multiple boxes. You can see a few ways I've tried to solve and test either of the two issues and I'm not having any luck. Any help with either issue would be appreciated.
ui <- fluidPage(
# Application title
titlePanel("Boxes"),
sidebarLayout(
sidebarPanel(
textOutput("text")),
# Get it it's a pun
mainPanel(
plotOutput("boxPlot",click = "test")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
vals <- reactiveValues(x=NA,y=NA,test=NA)
observeEvent(input$click, {
vals$x <- c(vals$x,input$test$x)
vals$y <- c(vals$y,input$test$y)
vals$test <- input$test$x
})
output$boxPlot <- renderPlot({
par(mai=c(0,0,0,0))
plot(1,ylim=c(2,15),xlim=c(2,15),type='n',yaxs='i',xaxs='i',ylab='',xlab='',axes=F)
for (i in 2:15) {
abline(v=i)
abline(h=i)
}
observeEvent(input$click, { rect(floor(input$test$x),floor(input$test$y),ceiling(input$test$x),ceiling(input$test$y),col='blue')})
# if (length(vals$x) > 0) {
# rect(floor(vals$x[1]),floor(vals$y[1]),ceiling(vals$x[1]),ceiling(vals$y[1]),col='blue')
# }
})
# output$text <- renderText(vals$x[length(vals$x)])
output$text <- renderText(vals$test)
}
# Run the application
shinyApp(ui = ui, server = server)
This might be a simple solution.
You should only have one single observeEvent for your click event. In that observeEvent, store your x and y values as reactiveValues as you current are doing.
Then, your renderPlot should plot the grid lines and filled in rectangles based on your reactiveValues. By including input$boxPlot_click (as I called it) in renderPlot the plot will be redrawn each click.
library(shiny)
ui <- fluidPage(
titlePanel("Boxes"),
sidebarLayout(
sidebarPanel(
textOutput("text")),
mainPanel(
plotOutput("boxPlot", click = "boxPlot_click")
)
)
)
server <- function(input, output) {
vals <- reactiveValues(x=NA,y=NA)
observeEvent(input$boxPlot_click, {
vals$x <- c(vals$x,input$boxPlot_click$x)
vals$y <- c(vals$y,input$boxPlot_click$y)
})
output$boxPlot <- renderPlot({
input$boxPlot_click
par(mai=c(0,0,0,0))
plot(1,ylim=c(2,15),xlim=c(2,15),type='n',yaxs='i',xaxs='i',ylab='',xlab='',axes=F)
for (i in 2:15) {
abline(v=i)
abline(h=i)
}
for (i in seq_along(length(vals$x))) {
rect(floor(vals$x),floor(vals$y),ceiling(vals$x),ceiling(vals$y),col='blue')
}
})
output$text <- renderText(paste0(vals$x, ', ' , vals$y, '\n'))
}
shinyApp(ui = ui, server = server)

R Shiny Link Multiple Inputs to Control 1 Output

I have a shiny App where I am displaying the same output multiple times. I have two inputs and they need to both control the same output. In my example below the outputs are copies of each other and it has to stay that way. Currently only the first input does anything. I need them to control the same output and react to changes in each other.
ui <- function(request) {
fluidPage(
textInput("txt1", "Enter text1"),
textInput("txt1", "Enter text2"),
checkboxInput("caps", "Capitalize"),
verbatimTextOutput("out1"),
verbatimTextOutput("out2"),
)
}
server <- function(input, output, session) {
output$out2<- output$out1 <- renderText({
if (input$caps)
toupper(input$txt1)
else
input$txt1
})
}
shinyApp(ui, server, enableBookmarking = "url")
You need to give your inputs unique IDs, but in your code both IDs are txt1. If you change this, you can use the normal reactivity:
library(shiny)
ui <- function(request) {
fluidPage(
textInput("txt1", "Enter text1"),
textInput("txt2", "Enter text2"),
checkboxInput("caps", "Capitalize"),
verbatimTextOutput("out1"),
verbatimTextOutput("out2"),
)
}
server <- function(input, output, session) {
output$out2<- output$out1 <- renderText({
if (input$caps)
paste(toupper(input$txt1), toupper(input$txt2))
else
paste(input$txt1, input$txt2)
})
}
shinyApp(ui, server, enableBookmarking = "url")
I have had a similar situation where I needed multiple identical inputs (albeit I only needed one output) that always have the same value.
The solution for me was to create a reactive element that holds the value for the inputs and syncs the value with the inputs.
Ie this code always makes input 1 and 2 have the same values
library(shiny)
ui <- fluidPage(
selectInput("id1", "Input 1", choices = c("A", "B")),
selectInput("id2", "Input 2", choices = c("A", "B")),
)
server <- function(input, output, session) {
# the reactive value always holds the value from the inputs
input_filter <- reactiveVal("A")
# sync from the reactive value to the inputs
observeEvent(input_filter(), {
print("input_filter() has changed")
updateSelectInput(session, "id1", selected = input_filter())
updateSelectInput(session, "id2", selected = input_filter())
})
# sync from the inputs to the reactive value
observeEvent(input$id1, {
print("Update id1")
input_filter(input$id1)
})
observeEvent(input$id2, {
print("Update id2")
input_filter(input$id2)
})
}
shinyApp(ui, server)

R Shiny won't output a variable using textOutput

I'm trying to write a calculator using Shiny in R for a video game (you input the stats of you and your opponent, and it outputs your odds of winning a match). However, I can't get the Shiny app to output any of my variables. The app runs fine, but nothing outputs when the action button is selected.
Trying to find the issue, I simplified my code into a basic calculator that takes a numeric input, multiplies it by two, and outputs a result. As before, nothing is displayed when the action button is pushed. However, if you directly type a string into the renderText function, it works just fine.
I need to include an action button in my ultimate code because I don't want it to calculate the result until several numerical values have been typed in. Could the action button be causing an issue somewhere, or is it something else?
Below is the simplified code. If I can get this to run, I'm sure I could get my more complicated code to run. Thank you!
library(shiny)
library(shinythemes)
ui <- fluidPage(
titlePanel("Multiply by 2"),
fluidRow(
column(12, textOutput("test"),
numericInput(inputId = "start", "Start", value = 1),
actionButton("go", "Go!") )
)
)
server <- function(input, output) {
myval <- reactiveValues()
observeEvent(input$go, {
reactive ({
if (input$go == 0)
return()
isolate({
myval$calc <- paste("The result is", 2*input$start)
})
})
})
output$test <- renderText({
if (input$go == 0)
return()
isolate({
myval$calc
})
})
}
shinyApp(ui = ui, server = server)
It looks like there is some extra code in there we don't need, for example the isolate function. See the below minimal example:
input$go doesn't tell us what the button is doing. Try running print(input$go) and have a look at the output.
library(shiny)
ui <- fluidPage(
titlePanel("Multiply by 2"),
fluidRow(
column(12,
textOutput("test"),
numericInput(inputId = "start", "Start", value = 1),
actionButton("go", "Go!")
)
)
)
server <- function(input, output) {
myval <- reactiveValues()
#Observe button (will run when the button is clicked)
observeEvent(input$go, {
myval$calc <- paste("The result is", 2 * input$start)
})
#Text output (will run when myval$calc changes)
output$test <- renderText({
myval$calc
})
}
shinyApp(ui = ui, server = server)

Dealing with nested selectizeInputs and modules

I am having trouble with nested selectizeInputs, i.e. a group of select inputs where the selection in the first determines the choices in the second, which control the choices in the third, and so on.
Let's say I have an select1 that lets you choose a dataset, and select2 which lets you pick a variable in the dataset. Obviously the choices in select2 depend on the selection in select1. I find that if a user selects a variable from select2, and then changes select1, it doesn't immediately wipe out the value from select2, but instead it goes through a reactive sequence with the new value in select1, and the old value from select2, which is suddenly referencing a variable in a different dataset, which is a problem.
Example:
library(shiny)
ui =fluidPage(
selectizeInput('d',choices=c('mtcars','iris'),
label="Datasets"),
uiOutput("vars"),
htmlOutput("out")
)
server = function(input, output, session) {
output$vars <- renderUI({
req(input$d)
selectizeInput("v",choices=names(get(input$d)), label="Variables",
options=list(onInitialize=I('function() {this.setValue("");}')))
})
output$out <- renderUI({
req(input$d,input$v)
HTML(paste0("The max is ",max(get(input$d)[[input$v]])))
})
}
runApp(list(ui = ui, server = server))
On launch, select mpg, and displays max value.
Now, after selecting mpg, if you switch to iris, you will get a barely noticeable error, then it corrects itself. This is a toy example, so the error is insignificant, but there could easily be cases where the error is much more dire (as is the case with the app I am currently developing).
Is there a way to handle nested selectizeInputs such that changes in an upstream selectizeInput won't evaluate with old values of down stream selectizeInputs when changed?
Thanks
edit: This issue turns out to have to do more with modules than anything else I believe:
library(shiny)
library(DT)
testModUI <- function(id) {
ns <- NS(id)
DT::dataTableOutput(ns("out"))
}
testMod <- function(input, output, session, data) {
output$out <- DT::renderDataTable({
data()
},caption="IN MODULE")
}
ui =fluidPage(
selectizeInput('d',choices=c('mtcars','iris'),
label="Datasets"),
uiOutput("vars"),
testModUI("test"),
DT::dataTableOutput("test2")
)
server = function(input, output, session) {
output$vars <- renderUI({
req(input$d)
selectizeInput("v",choices=names(get(input$d)), label="Variables",
options=list(onInitialize=I('function() {this.setValue("");}')))
})
observe({
req(input$d,input$v)#,get(input$d)[[input$v]])
validate(
need(input$v %in% names(get(input$d)), 'Wait.')
)
callModule(testMod,"test",reactive(data.frame(v1=max(get(input$d)[[input$v]]))))
})
output$test2 <- DT::renderDataTable({
req(input$d,input$v)#,get(input$d)[[input$v]])
validate(
need(input$v %in% names(get(input$d)), 'Wait.')
)
data.frame(v1=max(get(input$d)[[input$v]]))
},caption="OUTSIDE MODULE")
}
runApp(list(ui = ui, server = server))
Hello you can put condition to check if your code is going to run, here you just need that input$v to be a valid variable from input$d, so do :
output$out <- renderUI({
req(input$d,input$v)
if (input$v %in% names(get(input$d))) {
HTML(paste0("The max is ",max(get(input$d)[[input$v]])))
}
})
# or
output$out <- renderUI({
req(input$d,input$v)
validate(
need(input$v %in% names(get(input$d)), 'Wait.')
)
HTML(paste0("The max is ",max(get(input$d)[[input$v]])))
})
EDIT with module, you can define your module with an expression to validate like this :
testMod <- function(input, output, session, data, validExpr) {
output$out <- DT::renderDataTable({
validate(need(validExpr(), FALSE))
data()
},caption="IN MODULE")
}
And call the module in the server with the expression in a function :
observe({
req(input$d,input$v)
callModule(
module = testMod,
id = "test",
data = reactive({ data.frame(v1=max(get(input$d)[[input$v]])) }),
validExpr = function() input$v %in% names(get(input$d))
)
})

Resources