R Shiny Dynamically add textinput and print ui output based on Userinput - r

I am trying to create a shiny application which will enable users to add text boxes, or add images and create a document from it. I am able to add one Textbox and display its contents but when I add another textbox, the contents are not displayed. I have used a link as a starting point.
Here is my sample code that I am trying to add more user input text boxes by clicking add button.
library(shiny)
library(shinyjqui)
ui <- shinyUI(fluidPage(
sidebarPanel(
actionButton("add_btn", "Add Textbox"),
actionButton("rm_btn", "Remove Textbox"),
textOutput("counter")
),
mainPanel(
jqui_sortable(
div(id = 'textboxes',
uiOutput("textbox_ui"),
textInput("caption", "Caption", "Insert Text"),
verbatimTextOutput("value")
)
)
)
))
server <- shinyServer(function(input, output, session) {
# Track the number of input boxes to render
counter <- reactiveValues(n = 0)
#Track the number of input boxes previously
prevcount <- reactiveValues(n = 0)
observeEvent(input$add_btn, {
counter$n <- counter$n + 1
prevcount$n <- counter$n - 1})
observeEvent(input$rm_btn, {
if (counter$n > 0) {
counter$n <- counter$n - 1
prevcount$n <- counter$n + 1
}
})
output$value <- renderText({ input$caption })
output$counter <- renderPrint(print(counter$n))
textboxes <- reactive({
n <- counter$n
if (n > 0) {
# If the no. of textboxes previously where more than zero, then
#save the text inputs in those text boxes
if(prevcount$n > 0){
vals = c()
if(prevcount$n > n){
lesscnt <- n
isInc <- FALSE
}else{
lesscnt <- prevcount$n
isInc <- TRUE
}
for(i in 1:lesscnt){
inpid = paste0("textin",i)
vals[i] = input[[inpid]]
}
if(isInc){
vals <- c(vals, "Insert Text")
}
lapply(seq_len(n), function(i) {
textInput(inputId = paste0("textin", i),
label = paste0("Subsection ", i), value = vals[i])
})
}else{
lapply(seq_len(n), function(i) {
textInput(inputId = paste0("textin", i),
label = paste0("Subsection ", i), value = "Insert text")
})
}
}
})
output$textbox_ui <- renderUI({ textboxes() })
})
shinyApp(ui, server)
Any help will be appreciated in this regard. If anyone can point me in how to dynamically capture output$value everytime a new box is added it would push me in the right direction.

Have you tried reactiveValuesToList function ?
Here you have an example that might help
AllInputs <- reactive({
x <- reactiveValuesToList(input) })
textboxes <- reactive({
n <- counter$n
if (n > 0) {
isolate({
lapply(seq_len(n), function(i) {
textInput(inputId = paste0("textin", i),
label = paste0("Textbox", i),
value = AllInputs()[[paste0("textin", i)]])
})
})
}
})

Related

How to render a list of dataframes as tables to show as output in Shiny

I am working in a shiny app to compare multiple items according to an input defined by the user. The code works fine but I have an issue. I do not know what function I should apply in order to display the results of some computing as tables in the right side of the app. The code of the app is next:
library(shiny)
library(shinydashboard)
#Function
compute <- function(firstitem,seconditem)
{
Sum <- firstitem+seconditem
Difference <- firstitem+seconditem
Product <- firstitem*seconditem
Ratio <- firstitem/seconditem
Res <- data.frame(C1=Sum,C2=Difference,C3=Product,C4=Ratio)
return(Res)
}
#App
ui = shinyUI(fluidPage(
titlePanel("Compare"),
sidebarLayout(
sidebarPanel(
numericInput("numitems", label = "Number of items to compare?",
min = 1, max = 5, value = 1),
uiOutput("period_cutpoints"),
uiOutput("period_cutpoints2"),
actionButton("submit", "Submit")
),
mainPanel(
textOutput("numitems"),
textOutput("cutpoints")
)
)
))
server = shinyServer(function(input, output, session) {
output$period_cutpoints<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("firstitem",i),
label=paste0("Enter the value of first item ", i, ":"),value = 0)
})
})
output$period_cutpoints2<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("seconditem",i),
label=paste0("Enter the value of second item ", i, ":"),value = 0)
})
})
seldates <- reactiveValues(x=NULL)
observeEvent(input$submit, {
seldates$x <- list()
lapply(1:(input$numitems), function(i) {
seldates$x[[i]] <- compute(firstitem = input[[paste0("firstitem", i)]],seconditem = input[[paste0("seconditem", i)]])
})
})
output$cutpoints <- renderText({as.character(seldates$x)})
})
shinyApp(ui = ui, server = server)
It is working but my issue is that I do not know how to set the content of seldates, which are dataframes, as tables that should appear one after another. This task is done with output$cutpoints but I can not get them as Tables:
Does anybody know how can I fix this issue? Many thanks!
Try this
library(shiny)
library(shinydashboard)
library(DT)
#Function
compute <- function(firstitem,seconditem)
{
Sum <- firstitem+seconditem
Difference <- firstitem+seconditem
Product <- firstitem*seconditem
Ratio <- firstitem/seconditem
Res <- data.frame(C1=Sum,C2=Difference,C3=Product,C4=Ratio)
return(Res)
}
#App
ui = shinyUI(fluidPage(
titlePanel("Compare"),
sidebarLayout(
sidebarPanel(
numericInput("numitems", label = "Number of items to compare?",
min = 1, max = 5, value = 1),
uiOutput("period_cutpoints"),
uiOutput("period_cutpoints2"),
actionButton("submit", "Submit")
),
mainPanel(
textOutput("numitems"),
textOutput("cutpoints"),
uiOutput("t1")
)
)
))
server = shinyServer(function(input, output, session) {
output$period_cutpoints<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("firstitem",i),
label=paste0("Enter the value of first item ", i, ":"),value = i)
})
})
output$period_cutpoints2<-renderUI({
req(input$numitems)
lapply(1:(input$numitems), function(i) {
numericInput(inputId=paste0("seconditem",i),
label=paste0("Enter the value of second item ", i, ":"),value = i+i)
})
})
seldates <- reactiveValues(x=NULL)
observeEvent(input$submit, {
seldates$x <- list()
lapply(1:(input$numitems), function(i) {
seldates$x[[i]] <- compute(firstitem = input[[paste0("firstitem", i)]],seconditem = input[[paste0("seconditem", i)]])
})
})
output$cutpoints <- renderText({as.character(seldates$x)})
observeEvent(input$submit, {
lapply(1:(input$numitems), function(i) {
output[[paste0("table",i)]] <- renderDT(seldates$x[[i]])
})
output$t1 <- renderUI({
tagList(
lapply(1:(input$numitems), function(i) {
DTOutput(paste0("table",i))
})
)
})
})
})
shinyApp(ui = ui , server = server)

Using two reactives in shiny that depend on each other

I have been trying to create a dashboard with up to 3 inputs and then plot some data. I have done this part but the requirement now has changed that every time there is a selection of a new variable they should also be able to filter the data based on the new input. Here has been my attempt so far:
UI:
library(shiny)
ui <- fluidPage(
sidebarPanel(
tags$br(),
uiOutput("textbox_ui"),
uiOutput("filter_ui"),
tags$br(),
actionButton("add_btn", "Add Factor"),
actionButton("rm_btn", "Remove Factor"),
tags$br(),
actionButton("make","Create Graph and Tables")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Data stuff")
)
)
)
Server:
server <- function(input, output) {
# Track the number of input boxes to render
counter <- reactiveValues(n = 0)
AllInputs <- reactive({
x <- reactiveValuesToList(input)
})
observeEvent(input$add_btn, {
if(counter$n >2){
2
}else{
counter$n <- counter$n + 1
}
})
observeEvent(input$rm_btn, {
if (counter$n > 0) counter$n <- counter$n - 1
})
textboxes <- reactive({
n <- counter$n
if (n > 0) {
isolate({
lapply(seq_len(n), function(i) {
selectInput(inputId = paste0("var", i+1),
label = "",
choices = colnames(mtcars),
selected = AllInputs()[[paste0("var", i+1)]])
})
})
}
})
filterboxes <- reactive({
n <- counter$n
extrainputs <- sapply(seq_len(n), function(i) {
AllInputs()[[paste0("var", i+1)]]
})
summvar <- c(input$var1, extrainputs)
if(n > 0 ){
isolate({
lapply(1:length(summvar), function(x){
text <- summvar[x]
val_level <- unique(mtcars[[text]])
selectInput(inputId = paste0("fil",x+1),
label = paste0("Filter for ", text),
choices = val_level,
multiple = TRUE,
selected = val_level)
})
})
}
})
output$textbox_ui <- renderUI({ textboxes() })
output$filter_ui <- renderUI({ filterboxes() })
}
Two problems arise with this set up so far. One I cannot unselect any of the values when they appear in the filter second I see this warning on the sever side "Warning: Error in .subset2: invalid subscript type 'list'". My reactive skills are quite poor and any suggestions (reactive or not) would be appreciated.
As suggested in my comment...
library(shiny)
myfun <- function(df, var1) {
df %>% mutate(newvar = !!sym(var1)) # create newvar
}
ui <- fluidPage(
sidebarPanel(
tags$br(),
# uiOutput("textbox_ui"),
# uiOutput("filter_ui"),
tags$br(),
tags$div(id = 'placeholder'),
actionButton("add_btn", "Add Factor"),
actionButton("removeBtn", "Remove Factor"),
tags$br(),
actionButton("make","Create Graph and Tables")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Data stuff")
)
)
)
server <- function(input, output, session) {
# Track the number of variables
numvars <- reactiveVal(0)
### keep track of elements/lines inserted and not yet removed
inserted <- c()
observeEvent(input$add_btn, {
if(input$add_btn==0) {
return(NULL)
}
else {
if (numvars()<0) {
numvars(0) # clicking on remove button too many times yields negative number; reset it to zero
}
newValue <- numvars() + 1 # newValue <- rv$numvars + 1
numvars(newValue) # rv$numvars <- newValue
# btn needs to be adjusted if removing and adding factors
if (input$removeBtn==0){
btn <- input$add_btn
}else {
if (input$add_btn > input$removeBtn) {
btn <- input$add_btn - input$removeBtn # add_btn counter does not decrease
}else btn <- numvars()
}
id <- paste0('txt', btn)
insertUI(
selector = '#placeholder',
## wrap element in a div with id for ease of removal
ui = tags$div(
selectInput(inputId = paste0("var", btn),
label = "",
choices = colnames(mtcars)
),
selectInput(inputId = paste0("fil",btn),
label = paste0("Filter for ", id),
choices = "",
multiple = TRUE),
id = id
)
)
}
# inserted <<- c(id, inserted) ## removes first one first
inserted <<- c(inserted, id) ## removes last one first
}, ignoreInit = TRUE) ## end of observeevent for add_btn
observe({
#print(numvars())
lapply(1:numvars(), function(i){
observeEvent(input[[paste0("var",i)]], {
mydf <- mtcars
mydf2 <- myfun(mydf,input[[paste0("var",i)]])
mysub <- unique(mydf2$newvar)
nam <- as.character(input[[paste0("var",i)]])
updateSelectInput(session = session,
inputId = paste0("fil",i),
label = paste0("Filter for ", nam),
choices = mysub,
selected = mysub
)
})
})
})
observeEvent(input$removeBtn, {
newValue <- numvars() - 1
numvars(newValue)
removeUI(
## pass in appropriate div id
selector = paste0('#', inserted[length(inserted)])
)
inserted <<- inserted[-length(inserted)]
print(inserted)
}, ignoreInit = TRUE)
}
shinyApp(ui = ui, server = server)

Summing the values entered in textInput in RShiny

I am developing the Shiny app and I am unable to sum the values entered in dynamically created textInput.
The RCode used is as follows:
ui <- fluidPage(
fluidRow(
column(3, offset = 3,wellPanel(textOutput("text2"))),
column(3,wellPanel(textOutput("text3"))),
column(3,wellPanel(textOutput("text4")))
)
)
server <- function(input, output, session){
observeEvent(input$view, {
output$inputGroup = renderUI({
#code for generating textBoxes and corresponding Id's dynamically
input_list <- lapply(1:(nrow(df())*3), function(i) {
inputName <- paste("id", i, sep = "")
textInputRow<-function (inputId,value)
{
textAreaInput(inputName,"", width = "200px", height = "43px", resize = "horizontal")
}
column(4,
textInputRow(inputName, "")
)
})
do.call(tagList, input_list)
})
})
#code for adding the values and displaying the sum
output$text2 <- renderText({
tot = nrow(df())*3
sum1 = 0
for(lim in 1:tot){
if(lim %% 3 == 1){
inp = paste("id",lim)
sum1 = sum1 + input[[inp]]
}
}
})
}
shinyApp(ui = ui, server = server)
The output is :
Can anyone help me with this code?
While your question is modified, Here's a reproducible code for summing values entered in the textbox:
## Only run examples in interactive R sessions
if (interactive()) {
ui <- fluidPage(
textInput("input1", "Input1", 1),
textInput("input2", "Input2", 2),
tags$h3('Result:'),
verbatimTextOutput("value")
)
server <- function(input, output) {
output$value <- renderText({ as.numeric(input$input1) + as.numeric(input$input2)})
}
shinyApp(ui, server)
}

change label of shiny button and counting clicks

I was trying to switch the label of a show/hide columns button, and also keep the track of the number of times it is clicked in order to alter the number of columns showed of a table. I made it, but I couldn't use a direct even/odd differentiation of the value of the counter. Instead I had to use this: (vars$counter+1)/2) %% 2 == 0) to make it work, because each click changes the counter 2 times. I would like to request an easier procedure, maybe there is a shinyBS for that?
## app.R ##
library(shiny)
library(shinydashboard)
library(DT)
body<-dashboardBody(
textOutput("count"),
uiOutput('showallcolumnsbutton'),
DT::dataTableOutput('table2')
)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
body
)
server <- function(input, output) {
table<-data.frame(replicate(10,sample(0:1,1000,rep=TRUE)))
vars<-reactiveValues()
vars = reactiveValues(counter = 0)
observe({
if(!is.null(input$showallcolumns)){
input$showallcolumns
isolate({
vars$counter <- vars$counter + 1
})
}
})
label <- reactive({
if(!is.null(input$showallcolumns)){
if( ( (vars$counter+1)/2) %% 2 == 0) label <- "Hide"
else label <- "Show"
}
})
output$showallcolumnsbutton <- renderUI({
actionButton("showallcolumns", label = label(),
icon("hand-pointer-o"),
style="color: #000; background-color: #0099ff; border-color: #2e6da4"
)
})
output$count<-renderText({paste("counter value:",vars$counter)})
columnstoshow = reactive ({
x= ((vars$counter+1)/2) # %% 2 == 0)
if (!is.null (x))
{
if (x %% 2 == 0) {
c=c(1:10)
}
else {
c=c(1:5)
}
} #end 1st if
else {
c=c(1:10)
}
})
output$table2 = DT::renderDataTable({
DT::datatable(table[, columnstoshow()])
})
} # end server
shinyApp(ui, server)
Since Im not 100% what you want, is this it? Note that I used other library such as shinyBS
rm(list = ls())
library(shiny)
library(shinydashboard)
library(DT)
library(shinyBS)
body <- dashboardBody(bsButton("showallcolumns", label = "Hide", block = F, style="danger",icon=icon("hand-pointer-o")),br(),DT::dataTableOutput('table2'))
ui <- dashboardPage(dashboardHeader(),dashboardSidebar(),body)
server <- function(input, output,session) {
table <- data.frame(replicate(10,sample(0:1,1000,rep=TRUE)))
vars <- reactiveValues(counter = 1:10)
observeEvent(input$showallcolumns,{
if(input$showallcolumns %% 2){
updateButton(session, "showallcolumns",label = "Show", block = F, style = "success",icon=icon("hand-pointer-o"))
vars$counter <- 1:5
}
else{
updateButton(session, "showallcolumns",label = "Hide", block = F, style = "danger",icon=icon("hand-pointer-o"))
vars$counter <- 1:10
}
})
output$table2 = DT::renderDataTable({
DT::datatable(table[, vars$counter])
})
} # end server
shinyApp(ui, server)

In Shiny text input, need updated text to align so that overflow text appears right-aligned

I've created a Shiny app that adds text either by typing in text to an input field or by pressing various buttons that add text to the input field. I've contained the input field in a div that has direction:ltr as a style, so when text is typed in it appears on the right as wanted. However, when the text is updated using updateTextInput and the buttons that add text, the overflow text eventually disappears on the right because it is left aligned. The direction style is obviously not helping in this regard, and it doesn't help to specify text-align:left either. Any help is appreciated, thanks.
strings <- c("on", "recommend", "tolerably", "my", "belonging", "or", "am", "mutual", "has", "cannot", "indeed", "now", "mutual", "jennings", "offended")
string.list = NULL
for (i in 1:3) {
rnum <- sample(1:length(strings), 1)
string.list <- c(string.list, strings[rnum])
}
string.list <- unlist(string.list)
# Define UI
ui = (fluidPage(align = "center",
div(textInput(inputId = "inputText", label = "Enter your text here:"), style="direction:ltr"),
#display dynamic UI
uiOutput("my_button", inline=TRUE),
uiOutput("button2", inline=TRUE),
uiOutput("button3", inline=TRUE),
p(verbatimTextOutput('text'), width = 200)
))
server = function(input, output, session){
#set up reactive text
text <- reactiveValues()
text$string.list <- string.list
#make dynamic button
output$my_button <- renderUI({
actionButton("action1", label = text$string.list[1])
})
output$button2 <- renderUI({
actionButton("action2", label = text$string.list[2])
})
output$button3 <- renderUI({
actionButton("action3", label = text$string.list[3])
})
output$text <- renderText({
text$text <- c(text$mainText, input$inputText)
})
observeEvent(input$action1, {
text$text <- paste(text$text, text$string.list[1])
output$text <- renderText({
text$text
})
updateTextInput(session = session, inputId = "inputText", value = text$text)
string.list = character(0)
for (i in 1:3) {
rnum <- sample(1:length(strings), 1)
string.list <- c(string.list, strings[rnum])
}
new.strings <- unlist(string.list)
text$string.list <- new.strings
})
observeEvent(input$action3, {
text$text <- paste(text$text, text$string.list[3])
output$text <- renderText({
text$text
})
updateTextInput(session = session, inputId = "inputText", value = text$text)
string.list = character(0)
for (i in 1:3) {
rnum <- sample(1:length(strings), 1)
string.list <- c(string.list, strings[rnum])
}
new.strings <- unlist(string.list)
text$string.list <- new.strings
})
observeEvent(input$action2, {
text$text <- paste(text$text, text$string.list[2])
output$text <- renderText({
text$text
})
updateTextInput(session = session, inputId = "inputText", value = text$text)
string.list = character(0)
for (i in 1:3) {
rnum <- sample(1:length(strings), 1)
string.list <- c(string.list, strings[rnum])
}
new.strings <- unlist(string.list)
text$string.list <- new.strings
})
}
runApp(list(ui = ui, server = server))

Resources