Dans l’article précédent nous avons vu les mécanismes de transfert de données entre l’application Shiny et ses modules.
Maintenant que les modules n’ont (presque plus) de secrets pour vous ; peut-être êtes-vous intéressé pour utiliser un même module plusieurs fois sans pour autant coder chaque appel ?
Dans cet article, nous verrons comment appeler dynamiquement un module et gérer ses outputs server
et/ou UI
avec 2 exemples :
- Récupérer l’
UI
d’un module apellé dynamiquement. - Récupérer l’
UI
&server
d’un module appelé dynamiquement.
Si vous souhaitez jouer les exemples en local
Tous les codes utilisés ici sont disponibles sur Github ardata-fr/Shiny-Modules-Tutorials.
Ce dépôt est un package R dont les modules sont les fonctions exportées. Les applications Shiny sont dans le dossier inst
.
Installer le package et lancer localement les applications :
# install.packages("remotes")
remotes::install_github("ardata-fr/Shiny-Modules-Tutorials")
library(shinyModulesTuto)
# Lister les applications disponibles
listEx()
# Lancer la 1ere application
runEx(listEx()[1])
Exemple 1 : Récupérer seulement l’output UI
Comme vous le savez, un module est composé de 2 fonctions. L’interface UI
& la logique server
.
Dans cet exemple, la logique server
ne retourne rien. Nous avons simplement à gérer les sorties UI
.
https://ardata.shinyapps.io/dynamic-call/ ou bien lancer la commande :
# Lancer localement l'application exemple
runEx("dynamic-call")
NB : Dans cet exemple, chaque sortie UI
est shinyWidgets::panel
.
Initialiser rv$all_ui
La reactive rv
est initialisée comme ci-dessous :
rv <- reactiveValues(all_ui = list())
Appel dynamique au travers d’un observeEvent
A chaque chargement d’un dataset (cf : incrémentation de data_mod1$trigger
), l’observeEvent
:
- Exécute la partie
server
logique du module
callModule(
module = data_info,
id = data_mod1$trigger,
data = data_mod1$data,
data_name = data_mod1$data_name
)
- Récupére la sortie
UI
dans la reactiverv$all_ui
rv$all_ui[[data_mod1$trigger]] <- data_infoUI(id = data_mod1$trigger)
Agencement des éléments UI
Dans cet exemple, chaque sortie UI
est un shinyWidgets::panel
, ainsi on peut les utiliser dans un tagList
:
output$all_results <- renderUI({
tagList(rv$all_ui)
})
Exemple 2 : Récupérer les sorties UI
& server
Est-ce que vous vous souvenez de l’application whole-app
dans l’article précédent ?
Elle est encore disponible en ligne ou vous pouvez la lancer localement :
# Lancer localement l'application exemple
runEx("whole-app")
Pour cet exemple, j’ai créé le module merge_modules
qui contient les modules utilisés dans whole-app
sauf load_data
.
L’objectif est de faire une application contenant le module load_data
pour envoyer les données vers le “super-module” merge_modules
.
Ce “super-module” merge_modules
retourne les parties UI
de tous les modules sous-jacents. Sa partie server
renvoie une reactiveValues
contenant le nom ainsi que le nombre de fonctions appliquées sur chaque jeu de données chargé.
L’application est disponible en ligne https://ardata.shinyapps.io/dynamic-call-whole-app/ ou bien vous pouvez la lancer localement :
# Lancer localement l'application exemple
runEx("dynamic-call-whole-app")
NB : L’application est organisée avec un tabsetPanel
, chaque sortie de merge_modules
est ajoutée au travers d’un nouveau tabPanel
. De plus, chaque tabPanel
contient un bouton de fermeture.
Initialisation des variables
trick <- reactiveVal(0)
res <- list()
obs_close <- list()
- La
reactiveVal
trick
est un entier qui s’incrémente à chaque appel du modulemerge_modules
. Elle est ensuite utilisée commeid
pourtabPanel
et pour les noms d’éléments de la listeres
. - La liste
res
contient les sortiesserver
de chaque appel au modulemerge_modules
. - La liste
obs_close
contient lesobservers
qui se déclenchent pour la fermeture destabPanel
.
Appel dynamique au travers d’un observeEvent
A chaque chargement de dataset (cf : incrémentation de data_mod1$trigger
), l’observeEvent
:
- Exécute la partie
server
logique du module et stocke le résultat dans la listeres
.
res[[paste(id)]] <<- callModule(
module = merge_modules,
id = id,
data = data,
name = name
)
- Récupère la sortie
UI
et crée directement untabPanel
(via la fonctionappendTab
) :
appendTab(
inputId = "all_tabs",
tabPanel(
title = tabTitle(name, id),
value = id,
tags$br(),
merge_modulesUI(id = id)
),
select = TRUE
)
- Ajoute un
observer
dans la listeobs_close
pour déclencher la fermeture dutabPanel
:
obs_close[[paste(id)]] <<- observe({
shinyjs::onclick(id = paste0("close", id), closeTab(id = id))
})
- Incrémente la
reactiveValue
trick
trick(trick() + 1)
NB : L’incrémentation de trick
délenche la mise à jour du renderUI
ui_summary
.
La
reactiveVal
trick
est indispensable pour mettre à jourui_summary
car la listeres
n’est pas reactive.
Utiliser les sorties server
dans l’application
La liste res
peut être utilisée n’importe où dans l’application. Cependant, il faut penser à référencer la reactiveVal
trick
afin que le contexte reactif se mette à jour lorsque res
est modifié.
# Summary of all opened tabs
output$ui_summary <- renderUI({
# Reference trick to update reactive context
fool <- trick()
zz <- lapply(res, reactiveValuesToList)
if (length(zz) == 0) {
content <- div(class = "warn ", "No variable loaded")
} else {
# Use output of module merge_modules :
# - name
# - nb_funs
content <- tags$ul(
lapply(zz, function(x) {
tags$li(paste(x$name, ":", x$nb_funs))
})
)
}
tagList(content)
})
Bonus : Fermer un tabPanel
Dans cet exemple, on peut “supprimer” l’appel à un module. Nous devons défaire les étapes réalisées au chapitre précédent :
- Supprimer le
tabPanel
correspondant (UI
part). - Supprimer l’élément correspondant dans la liste
res
(Server
part).
Ces 2 étapes sont réalisées dans la fonction closeTab
décrite ci-dessous. Cette fonction est lancée dans l’observer
créé dynamiquement et situé dans obs_close
:
closeTab <- function(id) {
# Remove tab from UI
removeTab(inputId = "all_tabs", target = paste(id))
# Remove related element from list
res[[paste(id)]] <<- NULL
# Increase trick to refresh output$ui_summary
trick(trick() + 1)
}
Le bouton de fermeture du tabPanel
est créé avec la fonction ci-dessous :
# Function to add a cross after tabPanel title
tabTitle <- function(name, id) {
tags$span(
name, HTML(" "),
tags$span(
id = paste0("close", id),
class = "close",
HTML("×")
)
)
}
Conclusion
L’appel dynamique à un module est très pratique lorsque celui-ci :
- Peut être exécuté un nombre de fois indéfini.
- Executé à partir d’une action utilisateur.
L’appel se fait ensuite dans un observeEvent
ou bien un observe
puis :
Si on a besoin de la sortie
server
alors la stocker comme nouvel élément d’une liste.Pour la sortie
UI
deux solutions :- Stocker la sortie
UI
dans unereactiveValues
(cf exemple 1). - Utiliser directement la sortie
UI
dans l’observer
(cf exemple 2).
- Stocker la sortie
Etre le maître des modules Shiny
Au travers de ces 3 articles, nous avons vu :
- Pourquoi utiliser les modules Shiny ? : Voici quelques cas où ils vous apporteront une précieuse aide.
- Transfert de reactive : Description des mécanismes de transfert de données entre l’application Shiny et ses modules.
- Appel dynamique de modules : Gérer l’appel dynamique aux modules.
J’espère que ces articles vous seront d’une grande aide pour que vous puissiez prendre un max de plaisir à développer vos applications Shiny !
Suivez nous: - Sites recommandés: R-bloggers R weekly Twitter #rstats Jobs for R-users