Home > Back-end >  Updating private but not UI in reactive element
Updating private but not UI in reactive element

Time:11-08

I have an R6 class that I am using to organize my shiny application. Essentially, I want to connect different R6 classes for an experimental interface I am creating and want to reuse my code. As a simplified working example, see the code below.

library(R6)
library(stringi)
library(shiny)

df <- data.frame(dp = c("dp1", "dp2", "dp3"), desc = c("problem 1", "problem 2", "problem 3"))

app <- R6::R6Class(classname = "App",
            private = list(
              #unique string id
              ..id = stringi::stri_rand_strings(1, 18),
              #the data to be iterated through
              ..df = df,
              #counter to update text
              ..counter = 1,
              #initiating the dp and desc
              ..dp = 'dp1',
              ..desc = 'problem 1',
              
              #the underlying server, to be created like a normal server
              .server = function(input, output, session){
                output$text <- renderText({ 
                  private$..desc
                })
                
                observeEvent(input$button, {
                  private$..counter <- private$..counter   1
                  self$update_private()
                  #check the private content since the print is not updating
                  print(private$..counter)
                  print(private$..dp)
                  print(private$..desc)
                })
              }
            ),
            public = list(
              #create names for ui elements
              button = NULL,
              text = NULL,
              initialize = function(){
                
                self$button <- self$get_id("button")
                self$text <- self$get_id("text")
                self$update_private()
              },
              
              #gives ui outputs unique names tied to the user's id
              get_id = function(name, ns = NS(NULL)){
                ns <- NS(ns(private$..id))
                id <- ns(name)
                return(id)
              },
              #automatically updates the private field based on the counter
              update_private = function(){
                if(private$..counter == 1){
                  private$..dp <- "dp1"
                } else if(private$..counter == 2){
                  private$..dp <- "dp2"
                } else{
                  private$..dp <- "dp3"
                }
                private$..desc <- private$..df[private$..df$dp == private$..dp, "desc"]
              },
              
              ui = function(){
                fluidPage(
                  h1("An Example"),
                  mainPanel(
                  textOutput(self$text)),
                  sidebarPanel(
                  shiny::actionButton(inputId = self$button, 
                                      label = 'Update!', 
                                      width = '100%'
                  ))
                  

                )
              },#end ui
              
              server = function(input, output, session){
                callModule(module = private$.server, id = private$..id)
              }
            )
)

test <- app$new()

ui <- test$ui()

server <- function(input, output, session) {
  test$server()
}
shinyApp(ui = ui, server = server)

What I want: when someone clicks the action button, the reactive ui will update and the desired text from the data frame will be sliced and displayed. What I am getting: the internal private data fields are updating but the reactive ui elements are not.

Any ideas what could be causing this or a workaround? I thought about externally trying to use the observe event and then reinitiating the class with a new counter number. But I also can't seem to figure out that option either.

Appreciate your help!

CodePudding user response:

For anyone that comes across this problem... I figured out that even though the private is updating, and even though render is technically a reactive environment, you need to have your data stored publically in a reactive field.

library(R6)
library(stringi)
library(shiny)

df <- data.frame(dp = c("dp1", "dp2", "dp3"), desc = c("problem 1", "problem 2", "problem 3"))

app <- R6::R6Class(classname = "App",
            private = list(
              #unique string id
              ..id = stringi::stri_rand_strings(1, 18),
              #the data to be iterated through
              ..df = df,
              #counter to update text
              ..counter = 0,
              #initiating the dp and desc
              ..dp = NA,
              ..desc = NA,
              
              #the underlying server, to be created like a normal server
              .server = function(input, output, session){
                
                output$text <- renderText({ 
                  self$desc$text
                })
                
                observeEvent(input$button, {
                  private$..counter <- private$..counter   1
                  
                  self$update_private()
                  self$desc$text <- private$..desc
                  #check the private content since the print is not updating
                  print(private$..counter)
                  print(private$..dp)
                  print(private$..desc)
                })
              }
            ),
            active = list(
              .counter = function(value){
                if(missing(value)){
                  private$..counter
                }else{
                  private$..counter <- value
                }
              }
            ),
            public = list(
              #create names for ui elements
              button = NULL,
              text = NULL,
              
              
              #Need this to update the text***************
              desc = reactiveValues(text = NA),
              
              initialize = function(counter = self$.counter){
                self$.counter <- counter
                self$button <- self$get_id("button")
                self$text <- self$get_id("text")
                self$update_private()
                self$desc$text <- private$..desc
              },
              
              #gives ui outputs unique names tied to the user's id
              get_id = function(name, ns = NS(NULL)){
                ns <- NS(ns(private$..id))
                id <- ns(name)
                return(id)
              },
              #automatically updates the private field based on the counter
              update_private = function(){
                if(private$..counter == 1){
                  private$..dp <- "dp1"
                } else if(private$..counter == 2){
                  private$..dp <- "dp2"
                } else{
                  private$..dp <- "dp3"
                }
                private$..desc <- private$..df[private$..df$dp == private$..dp, "desc"]
              },
              
              ui = function(){
                fluidPage(
                  h1("An Example"),
                  mainPanel(
                  textOutput(self$text)),
                  sidebarPanel(
                  shiny::actionButton(inputId = self$button, 
                                      label = 'Update!', 
                                      width = '100%'
                  ))
                  

                )
              },#end ui
              
              server = function(input, output, session){
                counter <- reactiveVal(private$..counter)
                callModule(module = private$.server, id = private$..id)
              }
            )
)

test <- app$new(counter = 1)
ui <- test$ui()


server <- function(input, output, session) {
  test$server()
}
shinyApp(ui = ui, server = server)
  • Related