R语言 使用e_on()在动态呈现的UI中触发事件

2w3rbyxf  于 2022-12-20  发布在  其他
关注(0)|答案(2)|浏览(111)

RShiny用户有一个问题。我开发了一个应用程序,通过操作按钮、下拉菜单和图形点击事件,它有多个渲染UI的路径。我的目标是完全模块化应用程序。
我遇到的模块化问题是在动态呈现的UI中使用echarts 4 r e_on(“点击”)功能。作为最小工作代码的一个例子(它反映了当前的过程),我使用了数据集iris:

library(shiny)
library(DT)
library(echarts4r)

ui <- fluidPage(
  fluidRow(br(),
           br(),
           actionButton("example_button", "Click button to see table"),
           echarts4rOutput("example_plot"),
           dataTableOutput("table_example"))
)

server <- function(input, output, session) {
  
  group <- reactiveVal(iris)
  
  output$example_plot <- renderEcharts4r({
    
    iris %>% 
      group_by(Species) %>% 
      e_charts(Sepal.Length) %>% 
      e_scatter(Petal.Length, Sepal.Width) %>% 
      e_on(
        "click",
        "function(){
          Shiny.setInputValue('example_plot_on_click', true, {priority: 'event'});
        }"
      )
    
    })
    
  observeEvent(input$example_plot_clicked_serie, {
    
    group(filter(iris, Species == input$example_plot_clicked_serie))
    
  })
  
  output$table_example <- renderDataTable({
    
    group()
    
    
  })
  
  
}

shinyApp(ui, server)

本质上,渲染一个echarts,点击一个点,然后输入一个React值。在这个例子中,当你点击一个物种时,它会过滤该物种的Iris数据集,并显示在DT表中。我根本不是JS方面的Maven,但我设法通过shiny. setInputValue实现了这一点。在我的应用程序中,操作按钮起到了类似的作用(例如过滤表格),但这是工作的,所以我没有包括在这里的示例代码。为了放在上下文中,这个用户界面和服务器出现在多个模态弹出窗口中,并为不同的组提供不同的按钮和图表(但每个模态的过程是相同的)每个按钮和图表(整个应用程序中有50-60个这样的组合)目前都有一个单独的调用:

output$setosa_plot <- renderEcharts4r({
    
    iris %>% 
      filter(Species == "Setosa") %>% 
      e_charts(Sepal.Length) %>% 
      e_scatter(Petal.Length, Sepal.Width) %>% 
      e_on(
        "click",
        "function(){
          Shiny.setInputValue('setosa_plot_on_click', true, {priority: 'event'});
        }"
      )
    
    }) 

  observeEvent(input$setosa_plot_clicked_serie, {

    group(filter(iris, Species == input$setosa_plot_clicked_serie))

  })

为了模块化,我打算动态呈现动作按钮和echart(因此每个模态都可以共享相同的代码)。我已经使动态渲染工作正常,作为一个例子,我已经为每个物种渲染了一个按钮和一个图表。我遇到的问题是我不能像在上面的例子中那样访问单击事件。我找不到解决方案。我尝试过不同的方法,我基本上需要返回在这些图中被点击的“物种”,以便在我的过滤函数中使用。

ui <- fluidPage(
  fluidRow(br(),
           br(),
           uiOutput("buttons_and_charts"),
           dataTableOutput("table_example"))
)

server <- function(input, output, session) {
  
  group <- reactiveVal(iris)
  
  
  output$buttons_and_charts <- renderUI({
    
    group_list <- unique(iris$Species)

    test <- list()
    
    for(i in unique(sort(group_list))){
      
      
      test[[i]] <- fluidRow(actionButton(paste0(tolower(i), "_button"), i),
                            iris %>% 
                              group_by(Species) %>% 
                              e_charts(Sepal.Length) %>% 
                              e_scatter(Petal.Length, Sepal.Width) %>% 
                              e_on(
                                "click",
                                "function(){ Shiny.setInputValue('example_plot_on_click', true, {priority: 'event'});
                                }"
                              )
      )
    }
    
    test
    
  })
  
  

  
  observeEvent(input$example_plot_clicked_serie, {
    
    group(filter(iris, Species == input$example_plot_clicked_serie))
    
  })
  
  output$table_example <- renderDataTable({
    
    group()
    
    
  })
  
  
}

shinyApp(ui, server)

我不知道这是否可能,但还是很高兴知道!提前感谢。

tkclm6bt

tkclm6bt1#

在此版本的应用中,您将查找任何要触发的按钮。在观察到的事件中,您将查看触发了哪个按钮。
这取决于从renderEcharts4r中为每个地块分配一个output(这仍将在renderUI内发生)。
我已经在你的ui的头部添加了一些样式标签。这对更改来说是不必要的。我想你的应用程序在左侧运行的原因是这个应用程序比这个问题中的内容更多。请随意删除tags$head中的所有内容。
除了style标签,你的ui没有改变,没有dataTableOutput,但这不是你问题的一部分。

library(shiny)
library(tidyverse)
library(echarts4r)

ui <- fluidPage(
  tags$head(tags$style(HTML(".row{margin-left: 50px;}"))),
  fluidRow(
      br(), br(),
      uiOutput("buttons_and_charts"),
      dataTableOutput("table_example"))
)

你的服务器有很大的不同。我已经添加了一些注解来澄清“什么”和“为什么”。

server <- function(input, output, session) {

  # make the group list a global sorted vector
  group_list <- unique(sort(iris$Species)) %>% as.character()

  # set all buttons to ONE event, which will be observed individually within this event
  btns <- reactiveValues(buttons = actionButton(inputId = 'btn1', label = 1))

  # make the buttons and make space, so that each plot can be assigned a
  #    unique OUTPUT  (as in output$thisplot)
  output$buttons_and_charts <- renderUI({
    test <- lapply(group_list, function(i) {
      plotname <- paste0("plt_", i)
      btns$buttons[[i]] <- actionButton(paste0(tolower(i), "_button"), i)
      fluidRow(btns$buttons[[i]],
               echarts4rOutput(plotname, height = "25vh", width = "40vh")) # create space
    })
    do.call(tagList, test) # render the buttons and plot SPACE
  })
  
  # update the plot space with individual output$...
  for(i in group_list) {
    plotname <- paste0("plt_", i)
    local({
      output[[plotname]] <- renderEcharts4r({
        iris %>% 
          group_by(Species) %>% 
          e_charts(Sepal.Length) %>% 
          e_scatter(Petal.Length, Sepal.Width)
      })
    })
  }
  
  # observe for ANY button
  observeEvent(btns$buttons, { 
    for(that in 1:length(btns$buttons)) {
      local({
        what <- group_list[that]              # observe for WHICH button
        observeEvent(eventExpr = input[[paste0(tolower(what), "_button")]],
                     handlerExpr = {
                       echarts4rProxy(paste0("plt_", what)) %>%
                         e_remove_serie(serie_name = what) # remove content
                     })
      })
    }
  })
}

第一节第一节第一节第一节第一次

camsedfj

camsedfj2#

因此,在反复使用代码和不同方法一段时间后,我找到了一种方法,确保动态呈现的按钮UI以及呈现的图形可以有单独的单击事件与之关联(在上面的代码中,它使用Iris数据集中的物种,而在我的代码中,组列表根据所单击的模态而变化)。
我使用这两个堆栈溢出线程作为基础:
Shiny - Can dynamically generated buttons act as trigger for an event
Dynamically add button UI and associated observeEvents
我的UI保持不变,每个图形都有一个动态呈现的按钮:

ui <- fluidPage(
  fluidRow(br(),
           br(),
           uiOutput("buttons_and_charts"),
           dataTableOutput("table_example"))
)

我的服务器与最初的尝试不同,应用了循环遍历列表的相同概念,但是为按钮和绘图单击提供了一个单击事件给空列表。

server <- function(input, output, session){
  
  
  group <- reactiveVal(iris)
  
  
  obsList <- list()
  graphNav <- list()
  
  output$buttons_and_charts <- renderUI({
    
    species_list <- sort(unique(iris$Species))
    
    lapply(species_list, function(i){
      
      btName <- paste0(tolower(i), "_button")
      graphName <- paste0(tolower(i), "_plot")
      
      if (is.null(obsList[[btName]])) {
        
        obsList[[btName]] <<- observeEvent(input[[btName]], {
          group(i)
        })
      }
      
      fluidRow(actionButton(btName, i, style="font-size: 100%"),
               echarts4rOutput(outputId = graphName, height = "50px")
      )
      
    }
    )
    
    
    
  })
  
  
  observe({
    
    species_list <- sort(unique(iris$Species))
    
    lapply(species_list, function(i){  
      
      
      local({  #because expressions are evaluated at app init
        
        ii <- i
        
        graphName <- paste0(tolower(ii), "_plot")
        graphNavigation <- paste0(tolower(ii), "_plot_clicked_serie") 
        
        group(NULL)
        
        if (is.null(graphNav[[graphNavigation]])) {
          
          graphNav[[graphNavigation]] <<- observeEvent(input[[graphNavigation]], {
            
            group(i)
            
          })
        }
        
        
        output[[paste0(graphName)]] <- renderEcharts4r({ 
          
          iris %>% 
            filter(Species == ii) %>% 
            e_charts(Sepal.Length) %>% 
            e_scatter(Petal.Length, Sepal.Width) %>% 
            e_on(
              "click",
              paste0("function(){ Shiny.setInputValue('", graphName, "on_click', true, {priority: 'event'});}")
            )
          
          
        })
      })
      
      
    })
    
    
    
    output$table_example <- renderDataTable({
      
      
     datatable(iris %>% 
                 filter(Species == group()))
      
      
    })
    
    
    
  })
  
  
}

shinyApp(ui, server)

因此,使用动态呈现的UI,您将获得一个按钮、一个图形和一个由上述单击事件确定的React值。该React是Iris中的3个种类之一,用于dataTableOutput中的过滤器。这正是我的上下文中所需的工作方式,因此希望能帮助其他人!也可以随时使用代码并改进它!

相关问题