I'm relatively new to Shiny. I'm trying to get my head around using R6 classes to share objects across different modules (as suggested in the Enginering Shiny book). I made a simple app to upload the palmerpenguins::penguins dataset as a .csv file and generate a plot.
I'm getting the following error when running the app:
Caused by error in `.data$bill_length_mm`:
! Column `bill_length_mm` not found in
`.data`.
I guess this is because the app is trying to plot the data before the dataset is actually uploaded. I thought I would need something like observeEvent() in the plotServer module but I'm still getting the error. Any idea what I'm doing wrong?
Confusingly, init("upload") actually triggers the flag, so your observeEvent isn't stopping that from triggering as you intend. See my examples in Unintentional triggering caused by `init()` · Issue #23 · ColinFay/gargoyle · GitHub You can get around that by adding req() inside your renderPlot but in your current version there is nothing that you can wait for other than using req(gargoyle::watch("upload") > 0) Is there a reason your data object is private? If you make that public instead then you could use that to block the execution. Also the reactivein uploadServer isn't doing what you think it is - that will never actually read the data in. You need to use observeEvent(input$file, to trigger the function when a file is uploaded.
It's usually not recommended to use public fields in OOP because anyone could then change the object stored in that field to whatever they like which can break the code (or in some cases retrieve sensitive data).
In my example, I could simply add a public method to get the data:
get_data = function() {
private$data
}
And then use req(r6$get_data()).
In that case, I would simply use a new environment to store your objects. All the beauty of OOP is to have mutable data and methods that interact with those data. I wouldn't use OOP if not for that purpose.
Yes, that shouldn't be an issue if your app is well designed I guess. When collaborating on an app, that would also limit the risk of having a collaborator breaking the code by adding something like this:
r6$data <- NULL
It's usually considered good practice to implement your own setter method to control how the data should be modified.
R6 classes are made up of environments. Using R6 classes only to store data is equivalent to using new.env(parent = emptyenv()) (or rlang::new_environment()). Nothing necessarily wrong with using R6 classes for that purpose but it's unecessary.