1

I have taken the example from Communication between modules and changed the second input module into a rendered one. So basically, once you click "Show" button, it renders the module UI and server. The module outputs selection items from 2 selectInputs in a list (list of 2 reactives). I have set up an observer for the first element as a minimal example for a problem I am facing. It fires only once but does not fire again after I select new values.

Interestingly, the examination of the module reactive output via the debug button browser() shows that the value indeed changes.

#' Variable selection for plot user interface
#'
#' @param id, character used to specify namespace, see \code{shiny::\link[shiny]{NS}}
#'
#' @return a \code{shiny::\link[shiny]{tagList}} containing UI elements
varselect_mod_ui <- function(id) {
    ns <- NS(id)
    
    # define choices for X and Y variable selection
    var_choices <- setNames(colnames(iris)[1:4], colnames(iris)[1:4])
    
    # assemble UI elements
    tagList(
        selectInput(
            ns("xvar"),
            "Select X variable",
            choices = var_choices,
            selected = colnames(iris)[1]
        ),
        selectInput(
            ns("yvar"),
            "Select Y variable",
            choices = var_choices,
            selected = colnames(iris)[2]
        )
    )
}

#' Variable selection module server-side processing
#'
#' @param input,output,session standard \code{shiny} boilerplate
#'
#' @return list with following components
#' \describe{
#'   \item{xvar}{reactive character indicating x variable selection}
#'   \item{yvar}{reactive character indicating y variable selection}
#' }
varselect_mod_server <- function(input, output, session) {
    
    return(
        list(
            xvar = reactive({ input$xvar }),
            yvar = reactive({ input$yvar })
        )
    )
}

#' Scatterplot module user interface
#'
#' @param id, character used to specify namespace, see \code{shiny::\link[shiny]{NS}}
#'
#' @return a \code{shiny::\link[shiny]{tagList}} containing UI elements
#' @export
#'
#' @examples
scatterplot_mod_ui <- function(id) {
    ns <- NS(id)
    
    tagList(
        fluidRow(
            column(
                width = 6,
                plotOutput(ns("plot1"))
            ),
            column(
                width = 6,
                plotOutput(ns("plot2"))
            )
        )
    )
}

#' Scatterplot module server-side processing
#'
#' This module produces a scatterplot with the sales price against a variable selected by the user.
#'
#' @param input,output,session standard \code{shiny} boilerplate
#' @param dataset data frame (non-reactive) with variables necessary for scatterplot
#' @param plot1_vars list containing reactive x-variable name (called `xvar`) and y-variable name (called `yvar`) for plot 1
#' @param plot2_vars list containing reactive x-variable name (called `xvar`) and y-variable name (called `yvar`) for plot 2
scatterplot_mod_server <- function(input,
                                   output,
                                   session,
                                   dataset,
                                   plot1vars,
                                   plot2vars) {
    
    plot1_obj <- reactive({
        p <- scatter_sales(dataset, xvar = plot1vars$xvar(), yvar = plot1vars$yvar())
        return(p)
    })
    
    plot2_obj <- reactive({
        p <- scatter_sales(dataset, xvar = plot2vars$xvar(), yvar = plot2vars$yvar())
        return(p)
    })
    
    output$plot1 <- renderPlot({
        plot1_obj()
    })
    
    output$plot2 <- renderPlot({
        plot2_obj()
    })
}


#' Produce scatterplot with variables selected by the user
#'
#' @param data data frame with variables necessary for scatterplot
#' @param xvar variable (string format) to be used on x-axis
#' @param yvar variable (string format) to be used on y-axis
#'
#' @return {\code{ggplot2} object for the scatterplot
#' @export
#'
#' @examples
#' plot_obj <- scatter_sales(data = ames, xvar = "Lot_Frontage", yvar = "Sale_Price")
#' plot_obj
scatter_sales <- function(dataset, xvar, yvar) {
    
    x <- rlang::sym(xvar)
    y <- rlang::sym(yvar)
    
    p <- ggplot(dataset, aes(x = !!x, y = !!y)) +
        geom_point() +
        theme(axis.title = element_text(size = rel(1.2)),
              axis.text = element_text(size = rel(1.1)))
    
    return(p)
}

# load packages
library(shiny)
library(AmesHousing)
library(dplyr)
library(rlang)
library(ggplot2)
library(scales)

# load separate module and function scripts
#source("modules.R")
#source("helpers.R")

# user interface
ui <- fluidPage(
    
    titlePanel("Iris Data Explorer"),
    
    fluidRow(
        column(
            width = 3,
            wellPanel(
                varselect_mod_ui("plot1_vars")
            )
        ),
        column(
            width = 5,
            scatterplot_mod_ui("plots")
        ),
        column(1, actionButton("show","Show"), actionButton("dbg","Debug")),
        column(
            width = 3,
            wellPanel(
                uiOutput("plot2_vars_ui")#varselect_mod_ui("plot2_vars")
            )
        )
    )
)

# server logic
server <- function(input, output, session) {
    observer = NULL
    # prepare dataset
    data <- iris
    
    # execute plot variable selection modules
    plot1vars <- callModule(varselect_mod_server, "plot1_vars")
    plot2vars <- list(xvar = reactive({colnames(iris)[1]}), yvar = reactive({colnames(iris)[2]}))#callModule(varselect_mod_server, "plot2_vars")
    
    observeEvent(input$show, {
        output$plot2_vars_ui = renderUI({
            
            plot2vars__ <<- callModule(varselect_mod_server, "plot2_vars")
            observer <<- observeEvent(plot2vars__$xvar, {
                print("observer inside renderUI is triggered!")
                print(plot2vars$xvar())
                #browser()
            })
            varselect_mod_ui("plot2_vars")
        })
    })
    
    observeEvent(plot2vars$xvar, {
        print("observer outside renderUI")
        print(plot2vars$xvar())
        #browser()
    })
    
    observeEvent(input$dbg, {
        
        browser()
    })
    # execute scatterplot module
    res <- callModule(scatterplot_mod_server,
                      "plots",
                      dataset = data,
                      plot1vars = plot1vars,
                      plot2vars = plot2vars)
}

# Run the application
shinyApp(ui = ui, server = server)
Nikoa
  • 11
  • 2
  • Welcome to SO! 441 lines is hardly a *minimal* working example, especially when you expect helpers to have access to a reasonably unusual package like `AmesHousing`. That considerably reduces the number of people able to help you. Personally, I would start by making `plot2vars` a `reactiveValues` (a "reactive list") rather than a simple list. Second, I suspect that nesting reactives, even with the global assignment, is likely to be problematic. I would change your workflow so that you didn't nest either the `callModule` or the `observeEvent` within the render UI. – Limey Jul 27 '22 at 14:29
  • Thanks, @Limey simplified code now and used iris data. – Nikoa Jul 27 '22 at 14:36
  • So you want the plot/selectInputs on the right to react in the same way as those on the left? Is that correct? – Limey Jul 27 '22 at 15:45
  • I can make no sense whatsoever of what you are trying to do. "once you click "Show" button, it renders the module UI and server". What? Why? How do you "render" a server? Why use `renderUI` when that's exactly what the module UI function does? You're "defining" an instance of the modeule server inside the `renderUI`, but not assigning its return value to any object - which would be scoped within the `renderUI` (unless the assignment is fudged with `<<-` or similar as you have done elsewhere) and therefore inaccessible to the rest of your code. ... – Limey Jul 27 '22 at 16:03
  • ... similarly the `observeEvent` defined within the `renderUI` is, I believe, outside the scope of Shiny's reactivity despite the assignment to `observer`. (See [here](https://shiny.rstudio.com/articles/understanding-reactivity.html).) That's why your "external" observer doesn't fire. Voting to close for lack of clarity but I will retract if you can explain what you are trying to do (and *why*) sufficiently well to make an answer possible. – Limey Jul 27 '22 at 16:06
  • @Limey yes, that's what I am trying to do. The reason for this is that I want to display the module under certain circumstances. These minimal examples do not cover the context. I would like to display the server only if the user acts in a certain way. Normally, I can insert input elements and listen to them dynamically. This is what I am trying here. I don't want this part of the application to be reactive either, hence the `observeEvent`s. – Nikoa Jul 27 '22 at 18:15
  • Nevertheless, I though I should be able to dynamically render and listen to a module. By the way, `observer` and the `plot2vars__` are accessible within the `input$dbg`s `observeEvent`. It is there but it does not trigger for some reason. – Nikoa Jul 27 '22 at 18:17

1 Answers1

0

Here I have reduced the issue even further in the example and my confusion was with observeEvent(input$show, { and observeEvent(plot_vars$xvar(), {. Reactive values require (), inputs don't.

#' Variable selection for plot user interface
#'
#' @param id, character used to specify namespace, see \code{shiny::\link[shiny]{NS}}
#'
#' @return a \code{shiny::\link[shiny]{tagList}} containing UI elements
varselect_mod_ui <- function(id) {
    ns <- NS(id)
    
    # define choices for X and Y variable selection
    var_choices <- setNames(colnames(iris)[1:4], colnames(iris)[1:4])
    
    # assemble UI elements
    tagList(
        selectInput(
            ns("xvar"),
            "Select X variable",
            choices = var_choices,
            selected = colnames(iris)[1]
        ),
        selectInput(
            ns("yvar"),
            "Select Y variable",
            choices = var_choices,
            selected = colnames(iris)[2]
        )
    )
}

#' Variable selection module server-side processing
#'
#' @param input,output,session standard \code{shiny} boilerplate
#'
#' @return list with following components
#' \describe{
#'   \item{xvar}{reactive character indicating x variable selection}
#'   \item{yvar}{reactive character indicating y variable selection}
#' }
varselect_mod_server <- function(input, output, session) {
    
    return(
        list(
            xvar = reactive({ input$xvar }),
            yvar = reactive({ input$yvar })
        )
    )
}

#' Scatterplot module user interface
#'
#' @param id, character used to specify namespace, see \code{shiny::\link[shiny]{NS}}
#'
#' @return a \code{shiny::\link[shiny]{tagList}} containing UI elements
#' @export
#'
#' @examples
scatterplot_mod_ui <- function(id) {
    ns <- NS(id)
    
    tagList(
        width = 6,
        plotOutput(ns("plot"))
    )
}

#' Scatterplot module server-side processing
#'
#' This module produces a scatterplot with 2 variables
#'
#' @param input,output,session standard \code{shiny} boilerplate
#' @param dataset data frame (non-reactive) with variables necessary for scatterplot
#' @param plot1_vars list containing reactive x-variable name (called `xvar`) and y-variable name (called `yvar`) for plot 1
#' @param plot2_vars list containing reactive x-variable name (called `xvar`) and y-variable name (called `yvar`) for plot 2
scatterplot_mod_server <- function(input,
                                   output,
                                   session,
                                   dataset,
                                   plotvars) {
    
    plot_obj <- reactive({
        p <- scatter_plot(dataset, xvar = plotvars$xvar(), yvar = plotvars$yvar())
        return(p)
    })
    
    output$plot <- renderPlot({
        plot_obj()
    })
}


#' Produce scatterplot with variables selected by the user
#'
#' @param data data frame with variables necessary for scatterplot
#' @param xvar variable (string format) to be used on x-axis
#' @param yvar variable (string format) to be used on y-axis
#'
#' @return {\code{ggplot2} object for the scatterplot
#' @export
#'
#' @examples
#' plot_obj <- scatter_sales(data = ames, xvar = "Lot_Frontage", yvar = "Sale_Price")
#' plot_obj
scatter_plot <- function(dataset, xvar, yvar) {
    
    x <- rlang::sym(xvar)
    y <- rlang::sym(yvar)
    
    p <- ggplot(dataset, aes(x = !!x, y = !!y)) +
        geom_point() +
        theme(axis.title = element_text(size = rel(1.2)),
              axis.text = element_text(size = rel(1.1)))
    
    return(p)
}

# load packages
library(shiny)
library(AmesHousing)
library(dplyr)
library(rlang)
library(ggplot2)
library(scales)

# user interface
ui <- fluidPage(
    
    titlePanel("Iris Data Explorer"),
    
    fluidRow(
        column(3, actionButton("show","Show"), actionButton("dbg","Debug"),
               textOutput("selection")),
        column(
            width = 3,
            wellPanel(
                uiOutput("plot_vars_ui")#varselect_mod_ui("plot2_vars")
            )
        ),
        column(
            width = 6,
            scatterplot_mod_ui("plots")
        )
    )
)

# server logic
server <- function(input, output, session) {
    observer = NULL
    # prepare dataset
    data <- iris
    
    # execute plot variable selection modules
    plot_vars <- list(xvar = reactive({colnames(iris)[1]}), yvar = reactive({colnames(iris)[2]}))#callModule(varselect_mod_server, "plot2_vars")
    
    observeEvent(input$show, {
        output$plot_vars_ui = renderUI({
            
            plot_vars <<- callModule(varselect_mod_server, "plot_vars")
            # observer <<- observeEvent(plot_vars$xvar, {
            #   browser()
            #   print("observer inside renderUI is triggered!")
            #   print(plot_vars$xvar())
            #   #browser()
            # })
            
            #observe({#plot_vars$xvar
            observeEvent(plot_vars$xvar(), {
                #browser()
                print("observer inside renderUI is triggered!")
                print(plot_vars$xvar())
                #browser()
            })
            
            #output$selection = renderText({
            #   plot_vars$xvar()
            #})
            
            varselect_mod_ui("plot_vars")
        })
    })
    
    observeEvent(plot_vars$xvar, {
        print("observer outside renderUI")
        print(plot_vars$xvar())
        #browser()
    })
    
    observeEvent(input$dbg, {
        
        browser()
    })
    # execute scatterplot module
    res <- callModule(scatterplot_mod_server,
                      "plots",
                      dataset = data,
                      plotvars = plot_vars)
}

# Run the application
shinyApp(ui = ui, server = server)
Nikoa
  • 11
  • 2