Logique conditionnelle Shiny : if/else, switch, req et validate dans le server
Faites en sorte que le server calcule et affiche des choses différentes selon l’état des entrées — ramifiez un reactive avec if/else, associez un choix avec switch(), et protégez avec req() et validate(need()).
Les vraies applications ne font pas toujours la même chose. L’utilisateur choisit un type de graphique, une statistique de résumé, une méthode — et le server doit répondre différemment. Apprenez à ramifier côté serveur : if/else à l’intérieur d’un reactive ou d’un render, switch() pour associer un choix à l’un de plusieurs résultats, req() pour attendre une entrée, et validate(need()) pour afficher un message convivial plutôt qu’une erreur rouge. Plus : quand ramifier côté serveur versus masquer l’interface avec conditionalPanel().
- Ramifiez avec
if/elseà l’intérieur d’unreactive()ou d’unrender*(). Le corps est du R ordinaire : testezinput$...et renvoyez (ou dessinez) une chose ou une autre. Utilisez-le quand le choix comporte deux ou trois cas. - Associez un choix à un résultat avec
switch(). Quand unselectInputchoisit l’un de plusieurs graphiques, statistiques ou méthodes,switch(input$choice, "a" = ..., "b" = ...)est bien plus net qu’une longue chaîneif/else if. req()attend une entrée valide (truthy) — silencieusement.req(input$file)arrête le reactive jusqu’à ce que l’entrée ait une vraie valeur, de sorte que les sorties restent vides au lieu de produire une erreur sur un démarrage à vide.validate(need(cond, "message"))affiche un message convivial. Quand vous voulez que l’utilisateur voie un encouragement (“Téléversez un fichier pour commencer”) plutôt qu’une erreur rouge, validez à l’intérieur du render.- Ramification côté serveur versus
conditionalPanel(): ramifiez côté serveur pour calculer quelque chose de différent ; utilisezconditionalPanel()pour afficher ou masquer une interface déjà construite dans le navigateur.
Introduction
Une application utile fait rarement une seule chose figée. L’utilisateur fait passer un selectInput de « Histogram » à « Boxplot », ou de « Mean » à « Median » ou choisit une méthode statistique — et le server doit répondre différemment pour chaque choix. C’est la ramification côté serveur : le même reactive ou render produit une valeur ou un graphique différents selon input$....
Cela s’appuie directement sur la programmation réactive et le pipeline de données réactif. Là, le reactive renvoyait toujours le même type de résultat ; ici, le corps choisit quel résultat renvoyer. Quatre outils couvrent presque tous les cas : if/else, switch(), req(), et validate(need()).
Le code de cette page n’est pas exécuté ici — une application Shiny a besoin d’une session R active, elle ne peut donc pas tourner dans une page web statique. Copiez n’importe quel bloc dans un fichier app.R et exécutez-le en local avec shiny::runApp() (ou cliquez sur Run App dans RStudio). Chaque extrait se dépose dans la fonction server d’une application.
Ramifier avec if/else à l’intérieur d’un reactive ou d’un render
Le corps d’un reactive() ou d’un render*() est du R ordinaire, vous ramifiez donc avec if/else comme partout ailleurs. Testez une entrée, renvoyez une valeur ou une autre. Utilisez-le quand le choix comporte deux ou trois cas — une bascule, un drapeau, un oui/non.
Renvoyer une valeur différente selon l’état d’une entrée. Ici, un reactive de résumé rapporte la moyenne ou la médiane selon un bouton radio :
stat <- reactive({
if (input$measure == "mean") {
mean(mtcars$mpg)
} else {
median(mtcars$mpg)
}
})
output$value <- renderText({ stat() })Dessiner un graphique différent selon l’état d’une entrée. La même idée à l’intérieur de renderPlot() — ramifiez sur une case à cocher pour ajouter une courbe de lissage ou non :
output$plot <- renderPlot({
plot(mtcars$wt, mtcars$mpg)
if (input$show_trend) {
abline(lm(mpg ~ wt, data = mtcars), col = "#3a86d4", lwd = 2)
}
})La première branche renvoie une valeur que la sortie affiche ; la seconde exécute un effet de bord (une ligne supplémentaire sur le graphique) uniquement quand la case est cochée. if/else se lit bien jusqu’à deux ou trois branches. Au-delà — un choix à quatre, cinq, six cas — la chaîne if/else if devient bruyante, et switch() est l’outil le plus net.
Associer un choix à l’un de plusieurs résultats avec switch()
Le cas Shiny classique : un selectInput laisse l’utilisateur choisir le type de graphique (ou la statistique de résumé, ou la méthode), et le server doit produire celui qui correspond. Une longue chaîne if/else if fonctionne mais se lit mal. switch() associe directement la valeur de l’entrée à un résultat — une ligne par cas, sans répéter input$... ==.
Choisir le type de graphique :
output$plot <- renderPlot({
switch(input$plot_type,
"hist" = hist(mtcars$mpg),
"box" = boxplot(mtcars$mpg),
"density" = plot(density(mtcars$mpg))
)
})Choisir la statistique de résumé :
output$value <- renderText({
switch(input$stat,
"mean" = mean(mtcars$mpg),
"median" = median(mtcars$mpg),
"sd" = sd(mtcars$mpg)
)
})Le premier argument est la valeur à comparer (input$plot_type) ; chaque bras nommé est un cas ; l’expression du bras qui correspond est évaluée et renvoyée. Ajoutez une méthode, ajoutez une ligne — pas de chaîne qui grossit. Vous pouvez donner à switch() un dernier argument sans nom comme valeur par défaut pour toute valeur qui ne correspond à rien :
label <- switch(input$plot_type,
"hist" = "Histogram",
"box" = "Boxplot",
"Unknown plot" # default — used when nothing above matches
)Faites appel à switch() chaque fois qu’une entrée choisit l’une de plusieurs options discrètes. C’est la réponse lisible au motif “choisir le X par un selectInput”.
Attendre une entrée avec req()
Au démarrage de l’application (ou quand l’utilisateur efface un contrôle), une entrée peut être NULL ou vide. Si le reactive s’exécute quand même, il produit généralement une erreur — et l’erreur clignote en rouge dans chaque sortie. req() arrête cela : il vérifie que ses arguments sont valides (truthy) et, si ce n’est pas le cas, s’arrête silencieusement de sorte que les sorties attendent simplement.
output$summary <- renderPrint({
req(input$file) # do nothing until a file is uploaded
d <- read.csv(input$file$datapath)
summary(d)
})Tant que input$file n’a pas de valeur, renderPrint ne s’exécute pas et la sortie reste vide — pas d’erreur rouge sur le démarrage à vide. Placez req() en tête, en listant toutes les entrées dont le corps a besoin. C’est la seule ligne qui fait qu’une sortie pilotée par une entrée se comporte bien avant que l’utilisateur n’ait rien choisi.
req() est silencieux par conception — c’est pour “l’utilisateur n’a simplement pas encore choisi”. Quand vous voulez plutôt dire à l’utilisateur quoi faire, il vous faut un message. C’est validate(need()).
Afficher un message convivial avec validate(need())
Une sortie vide convient pendant le chargement, mais parfois vous voulez guider l’utilisateur : “Téléversez un fichier pour commencer,” “Choisissez au moins un groupe.” validate(need(cond, "message")) vérifie une condition et, si elle échoue, affiche votre message à la place de la sortie — une instruction nette plutôt qu’une erreur rouge.
output$summary <- renderPrint({
validate(
need(input$file, "Upload a CSV file to begin.")
)
d <- read.csv(input$file$datapath)
summary(d)
})need(cond, message) renvoie NULL quand cond est valide (truthy) (le render se poursuit donc) et le message sinon (de sorte que validate() arrête le render et l’affiche). Empilez plusieurs appels need() pour vérifier plus d’une condition — le premier échec l’emporte :
output$plot <- renderPlot({
validate(
need(input$file, "Upload a CSV file to begin."),
need(input$xvar, "Choose a variable to plot.")
)
d <- read.csv(input$file$datapath)
hist(d[[input$xvar]])
})Le message apparaît dans la zone de sortie, mis en forme comme un avis, pas comme un plantage. Utilisez validate(need()) chaque fois que l’état vide ou invalide mérite une explication plutôt que le silence.
req() vs validate() : arrêt silencieux ou message visible
Tous deux arrêtent un reactive quand une condition n’est pas remplie — la différence est ce que voit l’utilisateur.
req()s’arrête silencieusement. La sortie reste vide. Utilisez-le pour “n’a pas encore choisi”, ou pour protéger des dépendances internes que l’utilisateur n’a pas à connaître.validate(need())s’arrête avec un message visible. Utilisez-le quand l’état vide/invalide doit s’expliquer de lui-même — “Téléversez un fichier,” “Sélectionnez au moins deux colonnes.”
Un motif courant utilise les deux : req() pour les entrées qui ne sont que de la plomberie, validate(need()) pour celle dont l’utilisateur est responsable.
output$result <- renderPlot({
req(input$dataset) # silent: internal, set by code
validate(need(input$group, "Choose a group.")) # visible: the user must pick
...
})Règle générale : req() pour le silence, validate() pour un encouragement.
Ramification côté serveur vs conditionalPanel() : calculer vs afficher/masquer
Il existe deux façons de rendre une application “conditionnelle”, et elles vivent de part et d’autre du fil.
- Ramifiez côté serveur (cette leçon —
if/else,switch()) quand vous devez calculer ou afficher quelque chose de différent : un graphique différent, une statistique différente, un modèle différent. Le server fait le travail et envoie un seul résultat. - Utilisez
conditionalPanel()(couvert dans l’interface dynamique) quand les sorties sont déjà construites et que vous voulez seulement en afficher ou en masquer une dans le navigateur — une bascule purement côté client, sans aller-retour au serveur. Sa condition est une petite expression JavaScript (input.plot_type == 'hist', un point, pas un$).
En bref : ramification côté serveur = calculer différemment ; conditionalPanel() = révéler une interface déjà construite. Beaucoup d’applications utilisent les deux — le server calcule le bon résultat, et conditionalPanel() affiche le panneau correspondant.
Quel outil choisir
| Vous voulez… | Faites appel à |
|---|---|
| Renvoyer l’une de deux/trois valeurs ou l’un de deux/trois graphiques selon une entrée | if/else à l’intérieur du reactive()/render*() |
Associer un choix de selectInput à l’un de plusieurs résultats |
switch(input$choice, "a" = ..., "b" = ...) |
| Attendre une entrée, silencieusement, avant de calculer | req(input$x) en tête du reactive |
| Afficher un message jusqu’à ce qu’une condition soit remplie | validate(need(input$x, "message")) |
| Afficher/masquer une interface déjà construite dans le navigateur | conditionalPanel() (interface dynamique) |
La règle générale : ramifiez côté serveur pour changer ce qui est calculé ; faites appel à conditionalPanel() pour changer ce qui est affiché. Et protégez chaque sortie pilotée par une entrée avec req() ou validate(need()) pour qu’elle se comporte bien sur un démarrage à vide.
Problèmes fréquents
validate(need()) n’affiche rien — la condition est fausse, ou elle n’est pas enveloppée. need() doit être à l’intérieur de validate(), et il n’arrête le render que lorsque sa condition est falsy (NULL, FALSE, "", un vecteur vide). Écrire need(input$file, "...") seul ne fait rien ; écrire validate(need(nrow(d) > 0, "No rows match.")) fonctionne. Si votre message n’apparaît jamais, vérifiez que la condition peut réellement être fausse et que need() se trouve à l’intérieur de validate().
req() vide silencieusement une sortie que vous vous attendiez à voir. req() s’arrête sur n’importe quelle valeur falsy — et 0, "", et FALSE sont tous falsy. Si req(input$n) est censé autoriser n = 0, il ne le fera pas : la sortie disparaît simplement sans erreur, ce qui est déroutant à déboguer. Quand 0/FALSE/"" sont valides, testez explicitement à la place — req(!is.null(input$n)) — pour que seule une entrée vraiment absente arrête le reactive.
Une longue chaîne if/else if que switch() simplifierait. Quatre branches if (input$type == "...") ou plus qui renvoient chacune un graphique ou une statistique sont difficiles à lire et faciles à casser. Si chaque branche compare la même entrée à une valeur fixe, c’est exactement à cela que sert switch(input$type, ...) — un bras par cas, une valeur par défaut claire, sans répétition.
Questions fréquentes
Ramifiez à l’intérieur du render. Pour deux ou trois cas, utilisez if/else : renderPlot({ if (input$type == "hist") hist(x) else boxplot(x) }). Quand une entrée choisit l’une de plusieurs options, switch() est plus net : switch(input$type, "hist" = hist(x), "box" = boxplot(x)). Le server calcule le résultat correspondant et l’envoie. Pour afficher ou masquer des sorties déjà construites plutôt que d’en calculer de différentes, utilisez conditionalPanel().
Tous deux arrêtent un reactive quand une condition n’est pas remplie, mais req() s’arrête silencieusement (la sortie reste vide) tandis que validate(need(cond, "message")) s’arrête avec un message visible dans la sortie. Utilisez req() pour “l’utilisateur n’a pas encore choisi” ou des gardes internes ; utilisez validate(need()) quand l’état vide ou invalide doit s’expliquer de lui-même, comme “Téléversez un fichier pour commencer.”
Appelez switch() avec la valeur de l’entrée d’abord, puis un bras nommé par choix : switch(input$plot_type, "hist" = hist(x), "box" = boxplot(x), "density" = plot(density(x))). Le bras correspondant s’exécute et sa valeur est renvoyée. Ajoutez un dernier argument sans nom comme valeur par défaut pour les valeurs non correspondantes. C’est le remplacement net d’une longue chaîne if/else if quand une entrée sélectionne l’une de plusieurs options.
Une entrée est NULL ou vide avant que l’utilisateur n’ait choisi, et votre reactive s’exécute quand même dessus. Protégez le haut de la sortie avec req(input$x) pour attendre silencieusement une vraie valeur, ou validate(need(input$x, "Choose an option.")) pour afficher un message à la place. Tous deux empêchent le corps de s’exécuter tant que l’entrée n’est pas prête.
Ramifiez dans le server (avec if/else ou switch()) quand vous devez calculer quelque chose de différent — un graphique, une statistique ou un modèle différent. Utilisez conditionalPanel() quand l’interface existe déjà et que vous voulez seulement en afficher ou masquer une partie dans le navigateur, sans aller-retour au serveur. La ramification côté serveur change ce qui est calculé ; conditionalPanel() change ce qui est affiché.
Testez vos connaissances
Partez d’une application dont l’interface a un selectInput("plot_type", "Plot", c("Histogram" = "hist", "Boxplot" = "box", "Density" = "density")) et un plotOutput("plot"). Écrivez le server pour que output$plot dessine l’histogramme, la boîte à moustaches ou le graphique de densité de mtcars$mpg selon le choix — en utilisant switch(), pas une chaîne if/else.
À l’intérieur de renderPlot({ ... }), appelez switch(input$plot_type, ...) avec un bras nommé par valeur : "hist", "box", "density". Chaque bras est l’appel de tracé pour ce cas.
server <- function(input, output, session) {
output$plot <- renderPlot({
switch(input$plot_type,
"hist" = hist(mtcars$mpg),
"box" = boxplot(mtcars$mpg),
"density" = plot(density(mtcars$mpg))
)
})
}switch() fait correspondre input$plot_type au bras nommé et l’exécute — une ligne par choix, facile à étendre avec un autre type de graphique, et bien plus clair que trois branches if/else if qui testent toutes la même entrée.
Vous voulez qu’une sortie affiche le message “Upload a file to begin” jusqu’à ce que l’utilisateur en téléverse un — pas une sortie vide ni une erreur rouge. Quel outil convient ?
A. req(input$file) en tête du render. B. validate(need(input$file, "Upload a file to begin.")) à l’intérieur du render. C. if (is.null(input$file)) return(NULL). D. conditionalPanel("input.file == null", ...).
B. validate(need(cond, "message")) affiche votre message à la place de la sortie quand la condition échoue — exactement l’encouragement visible que vous voulez. A (req()) s’arrête silencieusement, laissant la sortie vide sans message. C vide aussi la sortie silencieusement. D est un afficher/masquer côté client d’une interface déjà construite, pas un moyen d’afficher un message d’orientation dans une sortie.
Conclusion
La ramification côté serveur, c’est ainsi qu’une application Shiny fait la bonne chose pour chaque choix. Utilisez if/else pour deux ou trois cas, switch() pour associer un selectInput à l’un de plusieurs graphiques, statistiques ou méthodes, et protégez chaque sortie pilotée par une entrée : req() pour attendre silencieusement, validate(need()) pour afficher un message convivial. Et gardez la ligne claire — ramifiez côté serveur pour calculer quelque chose de différent ; utilisez conditionalPanel() pour afficher ou masquer une interface déjà construite. Ensemble, ils transforment une application figée en une application qui répond à l’utilisateur.
Ensuite, vous gérerez les cas que la ramification ne peut pas empêcher — erreurs, avertissements et entrées invalides — pour que l’application échoue avec élégance au lieu de planter.
Leçons connexes
- Construisez à partir d’ici : traitement des données — la leçon précédente, le pipeline de données réactif que cela ramifie ; programmation réactive pour le modèle
reactive()sous-jacent (le prérequis) ; et l’interface dynamique pour le pendantconditionalPanel()côté client. Ensuite la gestion des erreurs attrape ce que la ramification ne peut pas. - Pour aller plus loin : Logique du server Shiny — la série complète. · Shiny UI — la couche interface. · Science computationnelle & IA — le pilier.
Réutilisation
Citation
@online{2026,
author = {},
title = {Logique conditionnelle Shiny : if/else, switch, req et
validate dans le server},
date = {2026-06-28},
url = {https://www.datanovia.com/learn/programming/shiny/server/conditional-logic},
langid = {fr}
}