Question about Scoping for input objects in shiny application

I am in the process of developing a shiny application for data analysis, and have hit a problem when it comes to scoping. I had organized my application so that all my functions exist at the top of the application, and that the server only contained calls to those functions, and smaller work needed to make the program operate. I just identified a problem however, (access to input variables) with scoping that required me to place one of my function definitions inside the server function.

I had planned to later relocate all of the functions to a separate file. I was wondering if anyone had similar issues with regard to scoping, and if it is possible to define the scope of the function (i.e. allow it access to the variables without being placed in the server function).

Here is the specific error message I was addressing by moving the function into my server function.

Error : object 'input' not found

Apologies for the number of times the word function was used.

Thanks in advanced,

Chris

Hi,

Issues with Shiny code can stem from both the reactive Shiny code itself or the regular R code used in the functions. In order for us to help you with your question, please provide us a minimal reprocudible example (Reprex) where you provide a minimal (dummy) dataset and code that can recreate the issue. One we have that, we can go from there. For help on creating a Reprex, see this guide:

Good luck!
PJ

1 Like

Thanks for the added information. I didnt include any code because the question I had was more general, but now that I read over it I think an example could definitely help. Below you will find the function that I wish to place in the body of the shiny application source code, rather than within the server function. For further context, I have also included components in the UI and Server code to which it relies on. I hope this is an apt Reprex:

Code causing issues within the server function:

generate_values <- function(trunk,num) { 
    list <- vector("numeric", num)
    for (i in seq_len(num)) {
      list[i] <- input[[(paste0(trunk, i))]]
    }
    list
  }

Context code in the server:

output$input_panel <- renderUI({
    
    fluidRow(lapply(1:input$std_concentration, function(iter1){
      column(2, numericInput(paste0("std_conc", iter1), paste0("Concentration ", iter1), value = 1))
    }))
  })
  #server function 006 - render the summary page based on the inputs for trt_groups variable
  output$input_panel2 <- renderUI({
    fluidRow(lapply(1:input$trt_groups, function(iter1){
      column(2, textInput(paste0("trt_grp", iter1), paste0("Group ", iter1), value = 1))
    }))
  })
  #server action 007 - render the summary page based on the inputs for the trt_treatment variable
  output$input_panel3 <- renderUI({
    
    fluidRow(lapply(1:input$trt_treatments, function(iter1){
      column(2, textInput(paste0("trt_trt", iter1), paste0("Treatment ", iter1), value = 1))
    }))
  })

Context code in the UI:

tabPanel("Summary",
                 uiOutput("input_panel"),
                 uiOutput("input_panel2"), 
                 uiOutput("input_panel3")),

The question I have is in regard to the function generate_values(). There are no problems with it currently, but I would like to move it to the top of the document with the rest of my functions. I would like to know if there is a way to have a function which evaluates input variables outside of the server scope, so that the server function is merely calling the function generate_values() and does not need to house its definition also.

Thank you in advanced,

Chris

Hi,

It's not a full reprex yet where I can recreate the issue, but looking at your generate_values function it seems it is calling input without that being one of the arguments.

If this refers to the input variable from a Shiny session, than indeed this one will not be available outside the server function. Try adding it as an argument to your function and see what happens. I'm not sure if it will work as this input variable is a special one in Shiny...

generate_values <- function(trunk,num,input) { 
    list <- vector("numeric", num)
    for (i in seq_len(num)) {
      list[i] <- input[[(paste0(trunk, i))]]
    }
    list
  }

Hope this helps,
PJ

1 Like

Hi PJ,

This was the solution! Although I will say I am not quite sure why/ how. I suppose by allowing the server the set input (within its scope) as an argument to the function it allows it the be defined in the body of the R document but not within the actual server function.

Computer science is wild!

Thank you again for taking the time to review my method and fix the problem thats been haunting me for the last couple of days.

Best,

Chris Jones

Hi,

I'm not an expert at Shiny but here is my explanation:
Everything within the server function is part of one user session in Shiny. In other words, everyone who opens the app will get his own set of variables that are declared within the server function. Variables or functions declared outside the Shiny server function are shared between all sessions (and users). Given the input variable is unique per user, it is declared by Shiny within the server function and not outside. This means that functions outside the server function who need that variable can't see it unless it's explicitly passed to them through the arguments.

Hope this shines a bit more light on it :slight_smile:

1 Like

To complement, this is the official documentation for scoping rules in shiny
https://shiny.rstudio.com/articles/scoping.html

1 Like

Thank you! This document was exactly what I needed! Much appreciated.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.