首页 > 解决方案 > Shiny中观察者的执行顺序

问题描述

目标和当前方法

我有一个模块,它可以删除自己的 UI。我还希望能够从模块外部调用此终止功能,因此我将一个额外的响应传递给模块的服务器逻辑,并在此响应触发时调用终止例程。

到现在为止还挺好。现在我想实现一个函数,它首先杀死我的模块的所有实例,然后添加这个模块的新实例,这就是我为我选择的设计而苦苦挣扎的地方。

问题

我需要的是,在我的input$add5观察者添加任何新实例之前,所有旧实例都被杀死。这可以通过设置来完成kill_switch(TRUE),但问题是observer在我的模块中的 可以对此做出反应之前,input$add5观察者继续添加新模块,这将重置kill_switchFALSE并且基本上旧实例永远不会被杀死。

基本上,在当前的设计中,我希望observeronkill_switch在我更改标志后立即做出反应,并且只有在完成此操作后,我的add5观察者才会继续。

我需要的

我认为我的设计不是最优的,因此任何关于如何设置主应用程序和模块之间接口的建议都将受到高度赞赏。


代码

library(shiny)

boxer_ui <- function(id) {
  ns <- NS(id)
  div(
    id,
    id = ns("killme"), 
    style = "background-color:steelblue; font-size: xx-large; color: white")
}

boxer <- function(input, output, session, kill_switch) {
  ns <- session$ns
  observe({
    req(kill_switch())
    removeUI(paste0("#", ns("killme")))
  })
}

ui <- fluidPage(actionButton("new", "new"), 
                actionButton("killall", "Kill All"), 
                actionButton("add5", "Kill All & Add 5"),
                fluidRow(id = "content"))

server <- function(input, output, session) {
  ids <- reactiveVal(0)
  kill_switch <- reactiveVal(FALSE)
  handler <- reactiveValues()

  add_new <- function() {
    kill_switch(FALSE)
    ids(ids() + 1)
    new_id <- paste0("id", ids())
    insertUI("#content", "beforeEnd", boxer_ui(new_id))
    handler[[new_id]] <- callModule(boxer, new_id, kill_switch)
  }

  observeEvent(input$new, {
    isolate({
      add_new()
    })})

  observeEvent(input$add5, {
    isolate({
      kill_switch(TRUE)
      replicate(5, add_new())
    })})

  observeEvent(input$killall, kill_switch(TRUE))
}

shinyApp(ui, server)

标签: rshiny

解决方案


我能想到的一种解决方案是拆分remove/add如下:

server <- function(input, output, session) {
  ids <- reactiveVal(0)
  kill_switch <- reactiveVal(FALSE)
  add5 <- reactiveVal(FALSE)

  handler <- reactiveValues()

  add_new <- function() {
    kill_switch(FALSE)
    ids(ids() + 1)
    new_id <- paste0("id", ids())
    insertUI("#content", "beforeEnd", boxer_ui(new_id))
    handler[[new_id]] <- callModule(boxer, new_id, kill_switch)
  }

  observeEvent(input$new, {
    isolate({
      add_new()
    })})

  observeEvent(input$add5, {
    isolate({
      kill_switch(TRUE)
      add5(TRUE)
    })})

  observe({
      req(add5())
      isolate({
         replicate(5, add_new())
         add5(FALSE)
      })
  })

  observeEvent(input$killall, kill_switch(TRUE))
}

然而,这是基于observer永远不会被任何其他人打断的假设observer。真的吗?在这种情况下,我还可以添加priority参数以确保observer首先触发内部。

任何人都可以证明我的假设是正确的吗?


推荐阅读