2016-06-22 6 views
0

Ich verwende eine HTML-Vorlage für eine Shiny-Anwendung und versuche, die tabPanel-Funktionalität von Dashboards basierend auf der Fragment-ID des URI zu replizieren.

Speziell für eine Anzahl von Nav-Links, die als Seitenanker dienen, möchte ich, Shiny diese Fragment-ID, aktualisieren Sie alle serverseitigen Objekte nach Bedarf, und geben Sie die Ausgabe, die Pseudo-Registerkarte entspricht der Benutzer angeklickt auf. Dies basiert auf Joe Chengs college scorecard application (einfach die Abfragezeichenfolge für den Seitenanker austauschen). Ich würde diese Route auch lieber nicht mit einer Abfragezeichenfolge verwenden, da die App einen größeren Datensatz mit einer großen Anzahl von benutzerdefinierten Auswahlen erstellt, die in der URI codiert werden müssten. Da jede neue Suche eine Seitenaktualisierung auslöst, wäre dies wahrscheinlich eine nicht sehr gute Benutzererfahrung, wenn die Daten bei jeder Änderung der Eingaben erneut geladen, vorverarbeitet usw. werden.

Das Problem in I ausgeführt habe, ist, dass Shiny speichert nur der anfänglichen URI-Hash in session$clientData und es gibt keine Möglichkeit, die ich gefunden habe, um das zu aktualisieren zu zwingen (mit invalidateLater, eventReactive oder observeEvent), wenn der Benutzer navigiert von #summary bis #bleep bis #blorp. Hat jemand schon einmal auf ein ähnliches Problem gestoßen? Oder ist es möglich, das Objekt session$clientData ohne eine Aktualisierung der Seite zu aktualisieren?

Die andere Möglichkeit besteht darin, dem CSS einfach eine neue Klasse mit display: none; für versteckte divs hinzuzufügen und ein wenig JS zu schreiben, um die Dinge zu behandeln. Idealerweise würde ich jedoch lieber eine Shiny-basierte Lösung finden.

Mein Code sieht ähnlich aus wie:

index.html:

... 
<header role="banner"> 
    <h3 class="usa-display">My header!</h3> 
    <ul> 
    <li><a class="nav active" href="#summary">Summary</a></li> 
    <li><a class="nav" href="#blorp">Another Tab</a></li> 
    </ul> 
</header> 

<div data-display-if="output.appMode === 'summary' "> 
    {{ renderVerbatimText("clientInfo") }} 
</div> 

<div data-display-if="output.appMode === 'blorp' "> 
    <img src="assets/img/failWhale.jpg" /> 
</div> 
... 

ui.R:

function(req) { 
    htmlTemplate(
    "www/index.html" 
) 
} 

server.R:

parseHashString <- function (str, nested = FALSE) { 
    if (is.null(str) || nchar(str) == 0) { 
    values <- "summary" 
    return(values) 
    } 

    if (substr(str, 1, 1) == "#") { 
    str <- substr(str, 2, nchar(str)) 
    } 

    values <- strsplit(str, "#", fixed = TRUE)[[1]] 
    values <- values[!is.na(values)] 

    values <- values[values %in% c("summary", "blorp")] 

    return(values[1]) 
} 

shinyServer(function(input, output, session) { 
    output$appMode <- reactive({ 
    parseHashString(session$clientData$url_hash_initial) 
    }) 

    outputOptions(output, "appMode", suspendWhenHidden = FALSE) 


    output$clientInfo<- renderText({ 
    cdata <- session$clientData 
    cnames <- names(cdata) 

    allvalues <- lapply(cnames, function(name) { 
     paste(name, cdata[[name]], sep=" = ") 
    }) 
    paste(allvalues, collapse = "\n") 
    }) 
}) 

Antwort

0

Ich habe herausgefunden, dies nur durch Erstellen eines benutzerdefinierten Shiny-Eingabe. Im Grunde habe ich nur ein leeres/verstecktes Div erstellt, um den neuen Shiny-Input zu reflektieren. Obendrein ist es nur eine hübsche Standard-Eingabebindung per Rstudio's instructions. Die einzigen Änderungen, die ich vornehmen musste, waren, einen Rückruf zu jedem Zeitpunkt auszulösen, an dem sich der URI-Hashwert geändert hat (in diesem Fall, nachdem ein Benutzer auf einen der Nav-Links geklickt hat). Der nav click triggert on("hashchange" function() {...}), der dann input$appHash zum Aktualisieren zwingt. Was auch immer der neue URI-Hash ist, wird von jeder Logik, die ich in server.R habe, respektiert und die Ausgabe kann bedingt angezeigt werden.

index.html:

<!DOCTYPE html> 
<html lang="en"> 
    <head> 
    {{ headContent() }} 
    {{ bootstrapLib() }} 

    <script> 
     $(document).ready(function(){ 
     var hashBinding = new Shiny.InputBinding(); 
     $.extend(hashBinding, { 
      find: function(scope) { 
      return $(scope).find(".appHash"); 
      }, 
      getValue: function(el) { 
      return $(el).val(); 
      }, 
      setValue: function(el, value) { 
      $(el).val(value); 
      }, 
      subscribe: function(el, callback) { 
      $(el).on("change.hashBinding", function(e) { 
       $(el).val(window.location.hash); 
       callback(); 
      }); 
      }, 
      unsubscribe: function(el) { 
      $(el).off(".hashBinding"); 
      } 
     }); 

     Shiny.inputBindings.register(hashBinding); 

     $('.nav').click(function() { 
      $('.nav').removeClass('active'); 
      $(this).addClass('active'); 
     }); 

     $(window).on('hashchange', function() { 
      var el = $(".appHash"); 
      el.trigger("change"); 
     }); 
     }); 
    </script> 
    </head> 

    <body> 
    <header role="banner"> 
     <div class="appHash" id="appHash" style="display:none;"></div> 
     <ul> 
     <li><a class="nav active" href="#summary">Summary</a></li> 
     <li><a class="nav" href="#bleep">Tab X</a></li> 
     <li><a class="nav" href="#blorp">Tab Y</a></li> 
     <li><a class="nav" href="#faq">F.A.Q.</a></li> 
     </ul> 
    </header> 
    </body> 
</html> 
Verwandte Themen