Clare
Clare

Reputation: 79

Manipulating reactive objects in Shiny - Error in <-: invalid (NULL) left side of assignment

I'm running into the error "Error in <-: invalid (NULL) left side of assignment" over and over again as I attempt to take a reactive object in Shiny and further manipulate it. I've provided an illustrative example below.

 testdf <- data.frame(row1 = c(1,3,6), row2 = c(7, 5, 1))

 ui <- fluidPage(

   titlePanel("Education in Tanzania"),

   sidebarLayout(
     sidebarPanel(


       #Select aggregation level of data
       selectInput("AggregationSelect", 
                   label = "Aggregation",
                   choices = list("School" = 1, 
                                  "District" = 2, 
                                  "Region" = 3),
                   selected = 1)
     ),

     mainPanel(
       DT::dataTableOutput("OutputTable")
     )
   )
 )

 server <- function(input, output) {

     Output1 <- reactive({
         testdf
     })

   observe({
     if(2 %in% input$AggregationSelect) {
       observe({Output1()$name[3] <- "b"})
     } else  if(3 %in% input$AggregationSelect) {
       observe({Output1()$name[2] <- "c"})
     } else  if(1 %in% input$AggregationSelect) {
       observe({Output1()$name[1] <- "a"})
     }
   })

   output$OutputTable <- {DT::renderDataTable({
     DT::datatable(Output1(),
                   options = list(pagelength = 25,
                                  searching = TRUE,
                                  paging = TRUE,
                                  lengthChange = FALSE),
                   rownames = FALSE)
     })
   }
 }  

 shinyApp(ui = ui, server = server)

What I need to do in my actual code is assemble a dataframe through the UI (which I am able to do and therefore have just subbed a random df in here) and then add some information (represented here with the added "names" column) based on what has been selected in the UI. It seems like it shouldn't be all that difficult to add a column to a df, but within the reactive object context, nothing I have attempted has worked. Other ways to modify reactive objects are welcome as long as they can be applied to more complex multi-step scenarios - there's no way I can get everything I need bundled into the initial assignment of the reactive object.

Upvotes: 1

Views: 1494

Answers (3)

dracodoc
dracodoc

Reputation: 2763

  1. Reactive expressions cannot be modified from outside. You can only modify reactive values.

  2. Generally you should never need to use observe. Use reactive expression if you don't need side effect, use reactive values with observeEvent when needed.

  3. You must read reactive tutorials before going forward. There are quite some concepts need to be understood before you can do anything complex, especially the "force update habit". You need to let Shiny do the update properly and setup the logic correctly.

I suggest you read all the tutorials, articles about reactive in RStudio website, then watch the reactive tutorial video in Shiny conference.

Upvotes: 1

parth
parth

Reputation: 1621

As per my understanding of your problem, i've tweaked you code as following :

testdf <- data.frame(name = c(1,2,3), freq = c(100, 200, 300))

ui <- fluidPage(

titlePanel("Education in Tanzania"),

sidebarLayout(
sidebarPanel(


  #Select aggregation level of data
  selectInput("AggregationSelect", 
              label = "Aggregation",
              choices = list("School" = 1, 
                             "District" = 2, 
                             "Region" = 3))
),

mainPanel(
  DT::dataTableOutput("OutputTable")
 )
))

server <- function(input, output) {

Output1 <- reactive({
input$AggregationSelect
selection <- input$AggregationSelect

if(2 %in% selection){
  testdf$name[3] <- "b"
} 
else if(3 %in% selection){
  testdf$name[2] <- "c"
} 
else if(1 %in% selection){
  testdf$name[1] <- "a"
}
testdf
})

output$OutputTable <- {DT::renderDataTable({
DT::datatable(Output1(),
              options = list(pagelength = 25,
                             searching = TRUE,
                             paging = TRUE,
                             lengthChange = FALSE),
              rownames = FALSE)
})
}}  

shinyApp(ui = ui, server = server)

app

Upvotes: 0

Pork Chop
Pork Chop

Reputation: 29387

Im not 100% what you're doing but I think its best if you use eventReactive that would listen to your selectInput. Note that I added the variable names to the dataframe:

library(shiny)
testdf <- data.frame(names = c(1,3,6), row2 = c(7, 5, 1))

ui <- fluidPage(
  titlePanel("Education in Tanzania"),
  sidebarLayout(
    sidebarPanel(
      #Select aggregation level of data
      selectInput("AggregationSelect", label = "Aggregation",
                  choices = list("School" = 1, "District" = 2, "Region" = 3),selected = 1)
    ),
    mainPanel(
      DT::dataTableOutput("OutputTable")
    )
  )
)

server <- function(input, output) {

  Output1 <- eventReactive(input$AggregationSelect,{
    if(input$AggregationSelect %in% "2"){
      testdf$name[3] <- "b"
      return(testdf)
    }
    else if(input$AggregationSelect %in% "3"){
      testdf$name[2] <- "c"
      return(testdf)
    }
    else if(input$AggregationSelect %in% "1"){
      testdf$name[1] <- "a"
      return(testdf)
    }
    else{
      return(testdf)
    }
  })

  output$OutputTable <- {DT::renderDataTable({
    print(Output1())
    DT::datatable(Output1(),options = list(pagelength = 25,searching = TRUE,paging = TRUE,lengthChange = FALSE),rownames = FALSE)
  })
  }
}  

shinyApp(ui = ui, server = server)

enter image description here

Upvotes: 0

Related Questions