Dynamische UI-Elemente in Shiny - Teil 2


Bei statworx gehört das Bereitstellen unserer Projektergebnisse mithilfe von Shiny zu unserem täglichen Geschäft. Shiny ist eine großartige Möglichkeit, Benutzer mit ihren eigenen Daten und den von uns bereitgestellten Data-Science-Produkten interagieren zu lassen.
Die Philosophie der Reaktivität auf das UI Ihrer App anzuwenden, ist ein interessanter Weg, Ihre Apps näher an den Geist des Shiny-Pakets heranzuführen. Shiny wurde entwickelt, um reaktiv zu sein, warum sollte man dies also auf die Serverseite Ihrer App beschränken? Das Einführen dynamischer UI-Elemente in Ihre Apps hilft Ihnen, visuelle Unordnung zu reduzieren, für saubereren Code zu sorgen und das allgemeine Gefühl Ihrer Anwendungen zu verbessern.
Ich habe zuvor die Vorteile der Verwendung von renderUI
in Kombination mit lapply
und do.call
im ersten Teil dieser Serie über dynamische UI-Elemente in Shiny diskutiert. Aufbauend darauf möchte ich unsere Werkzeugkiste für reaktives UI-Design mit einigen weiteren Optionen erweitern.
Das Ziel
In diesem speziellen Fall versuchen wir, eine App zu erstellen, bei der eine der Eingaben dynamisch auf eine andere Eingabe reagiert. Nehmen wir an, wir möchten dem Benutzer mehrere Auswahlmöglichkeiten in Form eines selectInput
präsentieren. Nehmen wir auch an, dass eine der Optionen mehr Eingaben vom Benutzer erfordert, sagen wir einen Kommentar, um die vorherige Auswahl klarer zu erklären. Eine Möglichkeit, dies zu tun, wäre, ein statisches textInput
oder ähnliches zur App hinzuzufügen. Eine viel elegantere Lösung wäre, das zweite Eingabefeld bedingt zu rendern, sodass es nur erscheint, wenn die richtige Option ausgewählt wurde. Das folgende Bild zeigt, wie dies in der Praxis aussehen würde.shiny-app-dynamic-ui-elements

Es gibt mehrere Möglichkeiten, dies in Shiny zu erreichen. Ich möchte Ihnen zwei davon vorstellen, die beide zum gleichen Ergebnis führen, jedoch mit einigen wesentlichen Unterschieden zwischen ihnen.
Eine mögliche Lösung: req
Wofür req
normalerweise verwendet wird
req
ist eine Funktion aus dem Shiny-Paket, deren Zweck darin besteht, zu überprüfen, ob bestimmte Anforderungen erfüllt sind, bevor Sie mit Ihren Berechnungen innerhalb einer reaktiven Umgebung fortfahren. Normalerweise wird dies verwendet, um zu vermeiden, dass rote Fehlermeldungen in Ihrer ShinyApp-UI erscheinen, wenn ein Element Ihrer App von einer Eingabe abhängt, die noch keinen Wert hat. Möglicherweise haben Sie eine solche Fehlermeldung schon einmal gesehen:

Diese Fehler verschwinden normalerweise, sobald Sie den benötigten Eingaben einen Wert zugewiesen haben. req
sorgt dafür, dass Ihr gewünschtes Output erst berechnet wird, wenn die erforderlichen Eingaben festgelegt wurden, und bietet somit eine elegante Möglichkeit, die recht grell aussehenden Fehlermeldungen in der UI Ihrer App zu vermeiden.
Wie wir req
nutzen können
In Bezug auf reaktives UI-Design können wir die Funktionalität von req
nutzen, um bedingte Anweisungen zu unseren uiOutputs
hinzuzufügen. Dies wird durch die Verwendung von renderUI
und req
in Kombination erreicht, wie im folgenden Beispiel gezeigt:
output(dollar sign)conditional_comment <- renderUI({
# Bedingung spezifizieren
req(input(dollar sign)select == "B")
# Ausführen, nur wenn die Bedingung erfüllt ist
textAreaInput(inputId = "comment",
label = "Bitte fügen Sie einen Kommentar hinzu",
placeholder = "Hier Kommentar schreiben")
})
Innerhalb von req
wird die zu erfüllende Bedingung spezifiziert und der Rest des Codes innerhalb der reaktiven Umgebung, die von renderUI
erstellt wird, wird nur ausgeführt, wenn diese Bedingung erfüllt ist. Schön an dieser Lösung ist, dass, wenn die Bedingung nicht erfüllt ist, keine roten Fehlermeldungen oder andere visuelle Unordnung in Ihrer App erscheinen, genau wie am Anfang dieses Kapitels gesehen.
Eine einfache Beispiel-App
Hier ist der vollständige Code für eine kleine Beispiel-App:
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
selectInput(inputId = "select",
label = "Bitte wählen Sie eine Option",
choices = LETTERS[1:3]),
uiOutput("conditional_comment")
),
dashboardBody(
uiOutput("selection_text"),
uiOutput("comment_text")
)
)
server <- function(input, output) {
output(dollar sign)selection_text <- renderUI({
paste("Die ausgewählte Option ist", input(dollar sign)select)
})
output(dollar sign)conditional_comment <- renderUI({
req(input(dollar sign)select == "B")
textAreaInput(inputId = "comment",
label = "Bitte fügen Sie einen Kommentar hinzu",
placeholder = "Hier Kommentar schreiben")
})
output(dollar sign)comment_text <- renderText({
input(dollar sign)comment
})
}
shinyApp(ui = ui, server = server)
Wenn Sie dies selbst ausprobieren, werden Sie feststellen, dass das Kommentarfeld nicht versteckt oder deaktiviert ist, wenn es nicht angezeigt wird; es existiert einfach nicht, es sei denn, das selectInput
nimmt den Wert „B“ an. Das liegt daran, dass das uiOutput
-Objekt, das das gewünschte textAreaInput
enthält, nur gerendert wird, wenn die in req
angegebene Bedingung erfüllt ist.
Die beliebte Wahl: conditionalPanel
Von allen verfügbaren Werkzeugen für reaktives UI-Design ist dies wahrscheinlich das am weitesten verbreitete. Die mit conditionalPanel
erzielten Ergebnisse sind denen ziemlich ähnlich, die req
uns im obigen Beispiel ermöglicht hat, aber es gibt einige wesentliche Unterschiede.
Wie unterscheidet sich dies von req
?
conditionalPanel
wurde speziell entwickelt, um Shiny-Programmierern das bedingte Anzeigen oder Verbergen von UI-Elementen zu ermöglichen. Im Gegensatz zur req
-Methode wird conditionalPanel
innerhalb des UI-Teils der App ausgewertet, was bedeutet, dass es nicht auf renderUI
angewiesen ist, um die verschiedenen Eingaben des Shinyversums bedingt zu rendern. Aber warten Sie, Sie könnten fragen, wie kann Shiny Bedingungen in der UI-Seite der App auswerten? Wird das nicht immer im Server-Teil gemacht? Nun ja, das stimmt, wenn der Ausdruck in R geschrieben ist. Um dies zu umgehen, verlässt sich conditionalPanel
auf JavaScript, um seine Bedingungen auszuwerten. Nachdem Sie die Bedingung in JS angegeben haben, können Sie beliebige UI-Elemente zu Ihrem conditionalPanel
hinzufügen, wie unten gezeigt:
conditionalPanel(
# Bedingung spezifizieren
condition = "input.select == 'B'",
# Ausführen, nur wenn die Bedingung erfüllt ist
textAreaInput(inputId = "comment",
label = "Bitte fügen Sie einen Kommentar hinzu",
placeholder = "Hier Kommentar schreiben")
)
Dieses Code-Snippet zeigt das gleiche Verhalten wie das im letzten Kapitel gezeigte Beispiel mit einem wesentlichen Unterschied: Es ist jetzt Teil der UI-Funktion unserer ShinyApp, im Gegensatz zur req
-Lösung, die ein uiOutput
war, das im Server-Teil der App berechnet und später als Listenelement an unsere UI-Funktion übergeben wurde.
Eine einfache Beispiel-App:
Das Umschreiben der App, um conditionalPanel
anstelle von req
einzuschließen, ergibt ein Skript, das ungefähr so aussieht:
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
selectInput(inputId = "select",
label = "Bitte wählen Sie eine Option",
choices = LETTERS[1:3]),
conditionalPanel(
condition = "input.select == 'B'",
textAreaInput(inputId = "comment",
label = "Bitte fügen Sie einen Kommentar hinzu",
placeholder = "Hier Kommentar schreiben")
)
),
dashboardBody(
uiOutput("selection_text"),
textOutput("comment_text")
)
)
server <- function(input, output) {
output(dollar sign)selection_text <- renderUI({
paste("Die ausgewählte Option ist", input(dollar sign)select)
})
output(dollar sign)comment_text <- renderText({
input$(dollar sign)comment
})
}
shinyApp(ui = ui, server = server)
Mit diesen beiden einfachen Beispielen haben wir mehrere Möglichkeiten demonstriert, wie Ihre angezeigten UI-Elemente darauf reagieren können, wie ein Benutzer mit Ihrer App interagiert – sowohl auf der Server- als auch auf der UI-Seite der Anwendung. Um die Dinge einfach zu halten, habe ich ein einfaches textAreaInput
für diese Demonstration verwendet, aber sowohl renderUI
als auch conditionalPanel
können viel mehr als nur ein einfaches Eingabeelement enthalten.
Also werde kreativ und nutze diese Werkzeuge, vielleicht sogar in Kombination mit den Funktionen aus Teil 1 dieser Serie, um deine Apps noch glänzender zu machen!