Tuesday 9 April 2019

r - Reactive variable triggered too soon (on app start up) in Shiny Dashboard



I'm building an interactive map/dashboard in Shiny with lots of school district data. The goal is to build a sidebar that has various conditional panels, and when the user makes certain choices, the layers will be displayed on a map, and also that valueboxes appear below the map with some summarized stats. If you want to try to recreate the issue with the same data, I'm working with these two shapefiles (though in a separate script I merged in additional data and saved the 'schools' as a csv w/ lat&lng columns rather than a shp file):



http://schoolsdata2-tea-texas.opendata.arcgis.com/datasets/059432fd0dcb4a208974c235e837c94f_0



schoolsdata2-tea-texas.opendata.arcgis.com/datasets/e115fed14c0f4ca5b942dc3323626b1c_0



The first selectizeInput offers 3 layers: school districts, zipcode, counties. Once the "Districts" layer is selected, a conditional Selectize pops up and a list of districts to chose from. Once a district is selected, another conditional panel appears, offering a list of Campuses within the chosen district.




To provide the options for the "Campus" selectizeInput, I have a school_choices reactive variable that pulls the list of unique Campus names listed with the selected District.



The problem seems to be that on start up of the app, that variable is firing/triggering without any user input for the district. It also does not appear to be updating the selectizeInput when a district IS chosen.



I've tried nesting the reactive variable in an ObserveEvent as well as an Observe function with an if statement, but the same issue seems to be happening.



library(rgdal)
library(data.table)
library(readxl)
library(RPostgreSQL)

library(janitor)
library(rgeos)
library(here)
library(leaflet)
library(shiny)
library(shinyjs)
library(shinyWidgets)
library(shinyBS)
library(shinydashboard)
library(dplyr)

library(DT)
library(mapview)
library(png)

poly_options<- c("Independent School Districts", "Youth Crime Data by County", "Employment Data by Zip Code")
district_data <- readOGR(here("source_data/districts.shp"))
campus_data <- read.csv(here("source_data/schools.csv"))
district_options <- sort(district_data@data$NAME)




ui <- fluidPage(
useShinyjs(),
useShinydashboard(),
titlePanel("Awesome Title"),
sidebarLayout(
sidebarPanel(
selectizeInput(inputId = "polygons", label = "Display Info Rich layers:",
choices = c("Choose one " = "", as.character(poly_options)), multiple = TRUE),


conditionalPanel(condition = "output.districts",
selectizeInput(inputId = "schdist", label = "Choose a District:", choices = district_options,
multiple = FALSE, options = list(placeholder = 'Select one',
onInitialize = I('function() { this.setValue(""); }')))),

conditionalPanel(condition= "input.schdist", uiOutput("select_campus")),
)))

###


server <- function(input, output, session) {
school_choices <- reactive({
dist <- district_data[district_data@data$NAME == input$schdist, ]
choices <- campus_data[(campus_data$DISTNAME == dist$NAME), ]
choices <- choices$CAMPNAME
return(choices)
})

output$districts <- reactive({
if("Independent School Districts" %in% input$polygons) {

return(TRUE)
} else {
return(NULL)
}
})
outputOptions(output, "districts", suspendWhenHidden = FALSE)

output$select_campus <- renderUI({
selectizeInput("camp", "Choose a Campus:", choices = c("Choose one " = "", sort(school_choices())), selected = NULL)
})

}


I wrote in print statements to see when the reactive variable is triggered. What I expect is that the app will start up and the reactive variable will be NULL since there is no input yet, then as the user makes choices, the uiOutput will update. But what happened instead is that on start up the variable is triggered, and when there should be NULL districts selected (or only one at a time with the user's selection), instead it is printing that the selection is:



factor(0)
76 Levels: Aldine ISD Alief ISD Alvin ISD Anahuac ISD ... Willis ISD


Followed by this error:




Warning: Error in Ops.factor: level sets of factors are different
121: stop
120: Ops.factor
117: [C:\User\Documents\GitHub\project/app.R#822]


which I'm pretty sure it just erroring because the code doesn't work when more than one district is trying to be selected.



The rest of the app runs normally, except that when a user actually does select a district, the "Campus" selectizeInput doesn't update and stays blank.




Any thoughts as to why this variable is being triggered without user input?


Answer



Your example isn't reproducible because there's no code that loads your data (likely to be outside the ui and server parts). However, I think one issue is that there is no code that updates the selection input object. "renderUI" has ui elements that are typically rendered when certain conditions are met; the code in your example has no conditions attached.



Try something like the below instead of the renderUI function:



    updateSelectInput(session, "camp",
choices = schoolchoices(),
selected = input$camp)



Also, if updates to your map are still happening too fast then consider use of the isolate() function within the school_choices reactive expression. You could even isolate all reactives other than an action button.



# From https://shiny.rstudio.com/articles/isolation.html
# The plot render function changes only when the "goButton" button changes, rather than every time the input slider "obs" changes

server <- function(input, output) {
output$distPlot <- renderPlot({


# Take a dependency on input$goButton
input$goButton

# Use isolate() to avoid dependency on input$obs
dist <- isolate(rnorm(input$obs))
hist(dist)
})
}

No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...