2014-10-17 2 views
9

Nota: Dopo aver trovato la risposta ho riformulato la domanda per renderla più chiara.Posso salvare il vecchio valore di un oggetto reattivo quando cambia?

A volte in un'app shiny. Voglio utilizzare un valore selezionato dall'utente per un widget, nonché il valore precedente selezionato per lo stesso widget. Questo potrebbe applicarsi ai valori reattivi derivati ​​dall'input dell'utente, dove voglio il vecchio e il nuovo valore.

Il problema è che se provo a salvare il valore di un widget, la variabile che contiene quel valore deve essere reattiva o non si aggiornerà ogni volta che il widget cambia. Ma, se salvi il valore in un contesto reattivo, mi darà sempre il valore corrente, non il precedente.

Come posso salvare il valore precedente di un widget, ma mantenerlo aggiornato ogni volta che l'utente cambia il widget?

C'è un modo che non richiede l'uso di un actionButton ogni volta che l'utente cambia le cose? Evitare un actionButton può essere desiderabile con l'aggiunta di uno è altrimenti inutile e crea un clic in eccesso per l'utente.

+0

Il codice ha errori di battitura –

+0

@MatthewPlourde Grazie - Ho modificato la questione e ha finito per cercare di capire la mia risposta. La domanda non ha più codice. –

+0

Non puoi usare una variabile globale, definita prima di 'server'? Quindi all'interno del server, memorizzare i valori in questa variabile globale con '<< -' – Heisenberg

risposta

3

Visto che il metodo dell'evento di svuotamento della sessione sembra essere rotto per questo scopo, ecco un modo alternativo di farlo usando un costrutto observeEvent e una variabile reattiva.

library(shiny) 

ui <- fluidPage(
    h1("Memory"), 
    sidebarLayout(
    sidebarPanel(
     numericInput("val", "Next Value", 10) 
    ), 
    mainPanel(
     verbatimTextOutput("curval"), 
     verbatimTextOutput("lstval") 
    ) 
) 
) 

server <- function(input,output,session) { 
    rv <- reactiveValues(lstval=0,curval=0) 

    observeEvent(input$val, {rv$lstval <- rv$curval; rv$curval <- input$val}) 

    curre <- reactive({req(input$val); input$val; rv$curval}) 
    lstre <- reactive({req(input$val); input$val; rv$lstval}) 

    output$curval <- renderPrint({sprintf("cur:%d",curre())}) 
    output$lstval <- renderPrint({sprintf("lst:%d",lstre())}) 
} 
options(shiny.reactlog = TRUE) 
shinyApp(ui, server) 

Cedendo:

enter image description here

+0

Cosa intendi per "variabile globale" qui? Non sembri definire nulla in 'gloabl.r'. –

+0

Sì, un uso sciatto ma comune del termine "variabile globale". Corretto –

+0

Err, risulta che non è necessario utilizzare quell'operatore '<< -' se si utilizza una variabile reattiva. Freddo. Grazie per avermi incoraggiato. –

7

Aggiornamento Questa risposta è stata inviata prima dell'avvento del modello reactiveValues/observeEvent in shiny. Penso che la risposta di @MikeWise sia il modo migliore per farlo.

Dopo aver giocato un po ', questo è quello che mi è venuto in mente. L'ui.r è niente di speciale

ui.r

library(shiny) 

ui <- shinyUI(fluidPage(
    sidebarLayout(
    sidebarPanel(
     selectizeInput(inputId="XX", label="Choose a letter",choices=letters[1:5]) 
    ), 
    mainPanel(
     textOutput("Current"), 
     textOutput("old") 
    ) 
) 
)) 

"Current" mostrerà la selezione corrente e "old" visualizza la selezione precedente.

Nel server.r ho utilizzato tre funzioni chiave: reactiveValues, isolate e session$onFlush.

server.r

library(shiny) 

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

    Values<-reactiveValues(old="Start") 

    session$onFlush(once=FALSE, function(){ 
    isolate({ Values$old<-input$XX }) 
    }) 


    output$Current <- renderText({paste("Current:",input$XX)}) 

    output$old <- renderText({ paste("Old:",Values$old) }) 
} 

I server.r opere come questa.

Innanzitutto, Values$old viene creato utilizzando la funzione reactiveValues. Gli ho dato il valore "Start" per chiarire cosa stava succedendo durante il caricamento.

Quindi ho aggiunto una funzione session$onFlush. Si noti che ho session come argomento nella mia funzione server. Questa operazione verrà eseguita ogni volta che il sistema reattivo gocciolerà, ad esempio quando lo selectizeInput viene modificato dall'utente. Ciò che è importante è che verrà eseguito prima che input$XX riceva un nuovo valore, quindi il valore è stato modificato su selectizeInput ma non su XX.

All'interno dello session$onFlush Assegno quindi il valore in uscita di XX a Values$old. Questo viene fatto all'interno di un isolate() in quanto ciò preverrà eventuali problemi con input$XX viene aggiornato con i nuovi valori.Posso quindi utilizzare input$XX e Values$old nelle funzioni renderText().

+0

Sembra che lo script non funzioni. Ho avuto lo stesso valore per Vecchio e Corrente. –

+0

interrotto. Sembra che qualcosa di essenziale per questo lavoro sia cambiato. –

+0

@MikeWise Grazie, Questo è stato scritto pre 'observEvent' quindi è scaduto ora. –