Bilel Benaouda

Your article is ready to go. We recommend you read it.

Power Apps

Maîtriser la fonction LookUp

La fonction LookUp est l’une des plus utilisées dans Power Apps, mais aussi l’une des plus mal comprise. Elle permet de rechercher et retourner un enregistrement ou une valeur précise dans une table selon une condition. Apparemment simple, elle cache des subtilités de…

Bilel Benaouda6 min de lecture

La fonction LookUp est l’une des plus utilisées dans Power Apps, mais aussi l’une des plus mal comprise. Elle permet de rechercher et retourner un enregistrement ou une valeur précise dans une table selon une condition. Apparemment simple, elle cache des subtilités de performance, de délégation et de comportement qui peuvent faire la différence entre une app fluide et une app qui rame.

Dans ce guide technique, je vais tenter d’explorer :

  • Les fondamentaux et la syntaxe complète
  • Le modèle d’exécution et la délégation
  • Les patterns avancés et anti-patterns
  • L’optimisation des performances
  • Les cas d’usage réels en production

1. Anatomie de LookUp : au-delà de la syntaxe#

Syntaxe complète#

LookUp(Source; Condition; Result)

  • Source : Table, collection, ou source de données connectée
  • Condition : Expression booléenne évaluée pour chaque enregistrement
  • Result (optionnel) : Expression retournant la valeur souhaitée (pas seulement un nom de colonne)

Ce que retourne LookUp#

  • Si Result est omis → retourne l’enregistrement complet (record)
  • Si Result est spécifié → retourne la valeur évaluée (peut être une formule)
  • Si aucune correspondance → retourne Blank()
  • LookUp retourne toujours le premier enregistrement correspondant selon l’ordre de la source

⚠️ Point critique : L’ordre n’est PAS aléatoire, mais il est non déterministe si la source n’est pas explicitement triée. Sur SharePoint, c’est souvent l’ordre d’ID. Sur Dataverse, ça dépend de l’index utilisé.

2. Délégation : comprendre les limites réelles#

Qu’est-ce qui est délégable ?#

La délégation de LookUp dépend de trois facteurs :

  1. La source de données
  2. Les opérateurs utilisés dans la condition
  3. Les fonctions utilisées dans Result

Tableau de délégation par source#

Source LookUp délégable ? Opérateurs supportés Limite sans délégation
Dataverse ✅ Oui =, <>, <, >, <=, >=, And, Or, Not, In, StartsWith 500-2000 lignes
SharePoint ⚠️ Partiel =, <>, IsBlank uniquement 500-2000 lignes
SQL Server ✅ Oui Tous opérateurs standards 500-2000 lignes
Collections ❌ Non Tous (mais en local) Toutes les lignes (mémoire)
Excel ❌ Non Tous 500-2000 lignes

Exemple de délégation cassée sur SharePoint#

`// ❌ NON DÉLÉGABLE sur SharePoint (Contains non supporté) LookUp(Documents; Contains(Title; "rapport"); ‘Created By’)

// ✅ DÉLÉGABLE sur SharePoint LookUp(Documents; StartsWith(Title; "rapport"); ‘Created By’)`

Comment vérifier la délégation ?#

Dans Power Apps Studio, les formules non délégables affichent un **triangle ** avec l’avertissement "delegation warning".

3. Modèle d’exécution et performance#

Où et quand LookUp s’exécute#

Le contexte d’exécution de LookUp est crucial pour les performances :

Propriété Évaluation Impact performance Usage recommandé
OnSelect Une fois au clic ✅ Faible Idéal pour actions ponctuelles
OnVisible Au chargement de l’écran ✅ Faible Excellent pour initialisation
Default Au chargement du contrôle ⚠️ Moyen Acceptable si source < 1000 lignes
Text / Items À chaque rafraîchissement ❌ Élevé À ÉVITER absolument
DisplayMode / Visible À chaque interaction ❌ Très élevé Anti-pattern majeur

Erreur classique déjà observée#

Label1.Text = LookUp(Employees; Email = TextInput1.Text; FullName)

Pourquoi est-ce une erreur? Chaque caractère tapé réévalue la formule. Sur une table de 5000 employés non délégable, cela provoque un freeze de l’app.

Pattern correct#

` TextInput1.OnChange: Set(varEmployeeName; LookUp(Employees; Email = TextInput1.Text; FullName) )

Label1.Text: varEmployeeName`

4. Techniques avancées#

a) LookUp avec formule dans Result#

Result peut être une expression, pas seulement un nom de colonne :

`// Calcul direct dans Result LookUp(Orders; OrderID = 12345; Quantity * UnitPrice * (1 – Discount) )

// Concaténation LookUp(Employees; ID = varEmployeeID; FirstName & " " & LastName & " (" & Department & ")" )`

b) Gérer Blank() proprement#

Méthode classique avec IsBlank#

If( IsBlank(LookUp(Orders; Status = "Open"; OrderID)); "Aucune commande"; "Commande trouvée" )

Méthode moderne avec coalesce (//)#

LookUp(Orders; Status = "Open"; OrderID) // "N/A"

⚠️ Piège : Le coalesce ne fonctionne que si les types correspondent. Si OrderID est un nombre et "N/A" une chaîne, erreur de type.

c) LookUp dans Patch – attention à ce piège silencieux#

Patch(Orders; LookUp(Orders; OrderID = 9999); {Status: "Closed"} )

Problème : Si OrderID 9999 n’existe pas, LookUp retourne Blank(). Patch avec un premier argument Blank() crée un nouvel enregistrement au lieu de mettre à jour.

Solution robuste#

With( {record: LookUp(Orders; OrderID = 9999)}; If( !IsBlank(record); Patch(Orders; record; {Status: "Closed"}); Notify("Commande introuvable"; NotificationType.Error) ) )

d) LookUp vs First(Filter()) – quand choisir ?#

LookUp(Products; SKU = "A123") First(Filter(Products; SKU = "A123"))

Différences subtiles :

Critère LookUp First(Filter())
Lisibilité ✅ Plus clair Moins direct
Délégation Même capacité Même capacité
Chaînage Impossible Possible avec Sort()
Performance Identique Identique

Quand utiliser First(Filter()) :

// Besoin de trier avant de prendre le premier First( Sort( Filter(Orders; CustomerID = 1001); OrderDate; Descending ) )

LookUp seul ne permet pas de contrôler l’ordre. Si l’ordre importe, utilisez First(Sort(Filter())).

5. Relations Dataverse et navigation#

LookUp dans les relations many-to-one#

`// Navigation depuis un enregistrement vers sa relation Gallery1.Selected.Account.’Account Name’

// Équivalent avec LookUp (moins performant) LookUp(Accounts; ‘Account ID’ = Gallery1.Selected.’Account ID’; ‘Account Name’ )`

Recommandation : Sur Dataverse, privilégiez la navigation par relation native. C’est plus performant et plus lisible.

LookUp dans les choix (Choice columns)#

`// ❌ ERREUR FRÉQUENTE LookUp(Contacts; Status = "Active")

// ✅ CORRECT – Les choix sont des enums LookUp(Contacts; Status = Status.Active)`

6. LookUp imbriqués – à utiliser avec parcimonie#

// Récupérer le nom du manager du manager LookUp(Employees; ID = LookUp(Employees; ID = varCurrentEmployeeID; ManagerID ); FullName )

Problèmes :

  • Performance : Deux requêtes potentiellement non délégables
  • Lisibilité : Code difficile à maintenir
  • Debugging : Impossible de savoir quelle étape échoue

Alternative recommandée :

// Découper en variables Set(varManagerID; LookUp(Employees; ID = varCurrentEmployeeID; ManagerID) ); Set(varManagerOfManagerName; LookUp(Employees; ID = varManagerID; FullName) );

7. Patterns de cache et optimisation#

Pattern : Cache avec expiration#

`// OnVisible de l’écran If( DateDiff(varLastRefresh; Now(); Minutes) > 5 || IsBlank(varEmployeeData); Set(varEmployeeData; LookUp(Employees; Email = User().Email) ); Set(varLastRefresh; Now()) )

// Utilisation dans l’app Label1.Text = varEmployeeData.FullName Label2.Text = varEmployeeData.Department`

Avantages :

  • Une seule requête au lieu de N
  • Contrôle de la fraîcheur des données
  • Performance maximale

Pattern : Lazy loading conditionnel#

// Ne charge que si nécessaire If( IsBlank(varProductDetails); Set(varProductDetails; LookUp(Products; SKU = varSelectedSKU) ) )

8. Debugging et troubleshooting#

Techniques de débogage#

`// Afficher le résultat complet dans un Label Label_Debug.Text = JSON( LookUp(Orders; OrderID = 12345); JSONFormat.IncludeBinaryData )

// Vérifier si le résultat est Blank Label_Debug.Text = If( IsBlank(LookUp(Orders; OrderID = 12345)); "BLANK RESULT"; "FOUND" )

// Compter les résultats potentiels CountRows(Filter(Orders; Status = "Open"))`

Erreurs courantes et solutions#

Erreur Cause Solution
"Invalid argument type" Type mismatch dans condition Vérifier les types (Text vs Number)
"Network error" Délégation échouée Vérifier les limites de données
Résultat toujours Blank Condition jamais vraie Tester avec CountRows(Filter())
Performance dégradée LookUp dans propriété "live" Déplacer vers OnVisible/OnSelect

10. Checklist des bonnes pratiques#

✅ Toujours vérifier la délégation sur les sources > 2000 lignes

✅ Utiliser LookUp uniquement pour un résultat unique attendu

✅ Mettre en cache les résultats réutilisés (Set/With)

✅ Éviter LookUp dans Text/Visible/DisplayMode sans cache

✅ Gérer explicitement Blank() avec IsBlank ou //

✅ Sécuriser les Patch avec vérification IsBlank

✅ Préférer les relations natives sur Dataverse

✅ Découper les LookUp imbriqués en variables

✅ Tester avec des volumes réalistes (pas seulement 10 lignes)

✅ Documenter les hypothèses sur l’ordre des résultats

Conclusion#

La fonction LookUp est un outil fondamental de Power Apps, mais son utilisation efficace nécessite une compréhension approfondie de :

  1. La délégation et ses limites par source de données
  2. Le contexte d’exécution et son impact sur les performances
  3. La gestion des cas limites (Blank, types, erreurs)
  4. Les patterns d’optimisation (cache, lazy loading)

Règle d’or : LookUp est votre allié pour cibler rapidement une donnée précise, mais uniquement si vous anticipez ses comportements et optimisez son contexte d’exécution. Une formule LookUp mal placée peut transformer une app fluide en cauchemar utilisateur.

  • Power Apps

    Découvrez les Combobox Modernes

    5 min de lecture

    Le contrôle Modern Combobox (combobox moderne) est l’une des nouveautés “Modern Controls” de Power Apps dans les apps Canvas. Il remplace ou complète l’ancien combobox classique dans de nombreux scénarios. Mais qu’est‐ce qu’il apporte vraiment ? Quand l’utiliser, quelles sont…