为什么我的 Shiny (R) actionButton 在我使用不同的 actionLink 后没有响应?

Why doesn't my Shiny (R) actionButton respond after I use a different actionLink?

我正在编写一个 Shinyapp,使用户能够向 mongodb 输入新条目并从中删除特定行。

我正在尝试添加一项功能,允许通过保存该行的临时副本来撤消上次删除。貌似还可以,但是在我用了undo之后,不知道为什么删除按钮不能用了,我也想不通。

我认为这可能与我在其他一些地方对两个按钮使用观察者有关,但我不明白为什么这会导致任何问题(我需要它们来应用程序正常运行) - 无论如何,只要我不使用撤消功能,它们就不会阻止我一个接一个地删除几行。

正如您从下面的代码中看到的那样,我在整个代码中放置了一堆 print() 函数来尝试找出它的去向。奇怪的是 - none 人出现了!就像使用撤消后删除按钮根本不会激活脚本一样。有什么想法吗?

更新: 这是重现问题的 server.R 和 ui.R 的简短版本(不使用 mongodb):

server.R

tempEntry<-NULL
shinyServer(function(input, output, session) {
    dat<-data.frame(nums=1:3,ltrs=c("a","b","c"))
    ## Action: Delete entry
    output$delError<-renderText({
        input$delButton
        isolate({if (!is.na(input$delNum)) {
            tempEntry<<-dat[input$delNum,]
            output$undo<<-renderUI({
                 actionLink("undo","Undo last delete")
                })
            dat<<-dat[-input$delNum,]
            print("deleted")
            print(dat)
        } else print("nope2")
        })
    })

    ## Action: Undo delete
    output$undoError<-renderText({
        input$undo
        if (!is.null(input$undo)) {
        if (input$undo>0) {
        isolate({if (!is.null(tempEntry)) {
            dat<<-rbind(dat,tempEntry)
            tempEntry<<-NULL
            output$delError<<-renderText({""})
            print(dat)
        } else print("nope3")
        }) } else print("undo==0") } else print("undo null")
    })
})

ui.R:

library(shiny)
shinyUI(navbarPage("example",
                   tabPanel("moo",
                    titlePanel(""),
                    fluidPage(numericInput("delNum","Row to delete",value=NULL),
                             actionButton("delButton","Delete row"),
                             uiOutput("undo"),
                             div(p(textOutput("delError")),style="color:red"),
                             div(p(textOutput("undoError")),style="color:blue")
                             ))))          

(这样删除一行后也报错"argument 1 (type 'list') cannot be handled by 'cat'",不知道是什么原因。。。不过这个问题好像和那个无关)。

谢谢!

发生这种情况是因为 output$delError<<-renderText({""}) 代码用空表达式覆盖了原始 output$delError 表达式,因此 output$delError 不再在 input$delButton 上触发也就不足为奇了.


[更新]

OP 的应用程序使用 actionButtonactionLink 分别从数据库中删除和取消删除记录。 'delete' 按钮应该触发删除记录并显示删除结果的 delError 表达式(例如 'record deleted')。同样,'undelete' 按钮触发 undoError 表达式,将记录放回 table 并报告未删除的结果(例如 'record undeleted')。问题是 undoError 必须摆脱 delError 产生的输出,因为输出 'record deleted' 和 'record undeleted' 一起出现时没有多大意义,但是输出'record deleted' 只能通过 delError 表达式删除。

似乎可以通过修改delError 使其在按下'undelete' 按钮(或link)时隐藏其输出来解决此问题。但在这种情况下,delError 会在 'delete' 和 'undelete' 按钮上触发,但无法说明是哪个按钮导致了评估,因此它会在 [=48] 时尝试删除一条记录=] 按钮被按下!

下面的示例应用程序提供了一种通过使用存储上次操作状态的全局变量来解决此问题的方法。此状态由两个高优先级观察者生成(一个用于 'delete',另一个用于 'undelete'),它们还负责记录的实际 deleting/undeleting。观察者不会产生直接进入网页的输出,因此摆脱其他观察者产生的消息没有任何麻烦。相反,状态变量由一个简单的反应表达式显示。

server.R

tempEntry<-NULL
dat<-data.frame(nums=1:3,ltrs=c("a","b","c"))


shinyServer(function(input, output, session) {

    del.status <- NULL


    ##################
    ### Observers ####
    ##################

    delete.row <- observe({
        if (input$delButton ==0 ) return()  # we don't want to delete anything at start

        delNum <- isolate( input$delNum ) # this is the only thing that needs to be isolated
        if (is.na(delNum)) { 
            print('nope2')
            return() 
        }

        tempEntry <<- dat[delNum,]
        dat <<- dat[-delNum,]

        output$undo <<- renderUI( actionLink("undo","Undo last delete") )
        del.status <<- 'deleted'
    },priority=100) # make sure that del.status will be updated *before* the evaluation of output$delError



    undelete.row <- observe({
        if (is.null(input$undo) || input$undo==0) return() # trigger on undowe don't want to undelete anything at the beginning of the script

        dat <<- rbind(dat,tempEntry)
        tempEntry <<- NULL

        output$undo <<- renderUI("")
        del.status <<- 'undeleted'
    },priority=100)



    ##################
    ### Renderers ####
    ##################

    output$delError <- renderText({
        if (input$delButton == 0) return() # show nothing until first deletion
        input$undo                         # trigger on undo

        return(del.status)
    })

    output$show.table <- renderTable({ 
        input$delButton; input$undo        # trigger on delete/undelete buttons
        return(dat)
    })

})

ui.R

library(shiny)
shinyUI(
    navbarPage(
         "example"
       , tabPanel("moo"
       , titlePanel("")
       , fluidPage(
             numericInput("delNum","Row to delete",value=NULL)
            , div(p(textOutput("delError")),style="color:red")
            , actionButton("delButton","Delete row")
            , uiOutput("undo")
            , tableOutput('show.table')
            )
       )
    )
)