Datastores et données
Comment un écran charge, filtre et enregistre des données — datastores d'enregistrement et de liste, clés, filtres, pagination et les liaisons de données.
Un écran ne parle pas directement à la base de données : il parle à des datastores — des conteneurs de données de l'écran qui chargent des enregistrements à travers les APIs de table de l'app. Les widgets se lient aux datastores : une Table affiche les lignes d'un datastore de liste, les champs d'un formulaire lisent et écrivent dans un datastore d'enregistrement.
C'est le maillon entre deux chapitres : les APIs de table se créent sur le modèle de données (chapitre APIs & GraphQL) ; ici on y relie l'écran.
Les deux types de datastore
| Type | Ce qu'il charge | Pour quoi |
|---|---|---|
| Enregistrement | UN enregistrement (ou un nouvel enregistrement, vide) | Les formulaires : les champs se lient aux champs de l'enregistrement, et à la fin on enregistre. |
| Liste | Une collection d'enregistrements | Tables, listes, cartes, graphiques, kanbans, calendriers. |
Les datastores peuvent vivre à deux endroits :
- Sur l'écran — créés dans l'inspecteur sans sélection, dans la catégorie Données. Ils sont partagés : plusieurs widgets peuvent lire le même, et c'est ce qu'on utilise pour les formulaires et pour les relations maître-détail.
- Dans un widget — les widgets de données (Table, Graphique, KPI…) ont leur propre datastore dans leur catégorie Données. C'est le cas le plus courant pour des grilles et des graphiques indépendants.
Le moteur est le même aux deux endroits ; la seule différence tient aux sources de valeur disponibles dans les filtres (voir les liaisons).
Créer un datastore sur l'écran
- Cliquez sur une zone vide du canvas pour que l'inspecteur montre l'écran.
- Dans la catégorie Données, cliquez sur + enregistrement ou + liste.
- Cliquez sur le datastore créé pour ouvrir la fenêtre Configurer le datastore.
- Donnez-lui un Nom du datastore — c'est par ce nom que les widgets et le
code le trouvent (ex. :
conta,contas). - Dans API, choisissez l'API qui sert les données. Les champs de l'API deviennent disponibles pour les colonnes, les liaisons et les filtres. Dans un datastore de liste, les API de pipeline apparaissent aussi, avec le badge pipeline : elles renvoient la liste entière, sans filtres ni pagination.

Note
Sans APIs de table publiées, le sélecteur avertit : Aucune API de table publiée dans cette app. Créez d'abord l'API sur l'entité du modèle — c'est une étape du chapitre APIs & GraphQL.
Datastore d'enregistrement — quel enregistrement charger
Un datastore d'enregistrement répond à une question : quel enregistrement ? La réponse se donne dans Quel enregistrement charger (clé) :
- Cliquez sur + champ de la clé.
- Choisissez le champ (par défaut, la clé primaire), l'opérateur et la valeur —
typiquement un Param de la route : l'écran Fiche de Compte reçoit
iddans l'adresse et charge le compte portant cet id. - Plusieurs conditions forment une clé composée — toutes doivent correspondre.
Sans conditions, le datastore charge un nouvel enregistrement (vide) — c'est
ainsi que le même écran de formulaire sert à créer : ouvert sans id, il
commence vierge ; enregistré, il fait l'insertion.
Avec des conditions qui ne trouvent aucun enregistrement (un id qui n'existe
plus, ou hors de portée du rôle de la personne qui ouvre l'écran), l'app
prévient que l'enregistrement demandé n'existe pas et le formulaire
n'enregistre pas. Seule une clé saisie dans un champ de l'écran (une clé
naturelle, comme un code d'article) reste celle d'un nouvel enregistrement.
Datastore de liste — filtres et chargement
Filtres (where)
Les Filtres (where) sont des conditions appliquées chaque fois que les données sont lues — c'est ici qu'on limite ce qui vient de la base de données. Chaque condition est champ / opérateur / valeur ; + ajouter un filtre ajoute des conditions et + groupe crée des sous-groupes imbriqués, avec Toutes (AND) ou Au moins une (OR) pour décider comment ils se combinent.
Exemple de Gestion des Clients : l'écran Comptes filtre estado eq "activa" ;
le panneau « mes comptes » ajoute gestor eq → Session ▸ username.
Les opérateurs :
| Opérateur | Ce qu'il compare |
|---|---|
eq / neq |
Égal / différent de la valeur. neq inclut les enregistrements dont le champ est vide. |
contains, startsWith, endsWith |
Texte qui contient, commence ou finit par la valeur. |
gt, gte, lt, lte |
Supérieur, supérieur ou égal, inférieur, inférieur ou égal — nombres et dates. |
in / nin |
Parmi / aucun de une liste de valeurs. La valeur est une liste : plusieurs valeurs séparées par des virgules, ou celle d'une Liste à Sélection multiple liée par Widget — c'est ainsi qu'une table se filtre sur plusieurs centres ou plusieurs états à la fois. nin inclut les enregistrements dont le champ est vide. |
Une condition dont la valeur est vide (zone de recherche blanche, liste sans choix) ne filtre rien — l'écran montre tout jusqu'à ce que la personne choisisse.
Chaque valeur d'un filtre est convertie selon le type de la colonne : dans une colonne de texte, un numéro fiscal ou un code postal (« 0012 ») restent du texte, et dans une colonne décimale « 12,5 » est un nombre.
Chargement et page
| Option | Ce qu'elle fait |
|---|---|
| Tout charger | Ramène tous les enregistrements du filtre d'un coup — changer de page, trier et filtrer à l'écran devient instantané. |
| Une page à la fois | Interroge le serveur à chaque changement de page — pour les grandes tables, où tout ramener n'a pas de sens. |
| Par page | Combien de lignes sont visibles à la fois à l'écran — à ne pas confondre avec le nombre d'enregistrements lus. |
| Charger automatiquement | Lire les données dès l'ouverture de l'écran. Désactivez si vous préférez ne charger qu'après une action (un bouton « Rechercher », par exemple). |
Sans Charger automatiquement, la liste lit quand on le lui demande : un
reload() (sur un bouton « Rechercher », par exemple), un changement de page ou
de tri, ou un filtre appliqué. Changer un champ de l'écran ne la fait pas lire
toute seule.
Quand le filtre effectif change (un champ de l'écran, un paramètre, l'état de l'app), la liste revient à la première page. Si la page où vous étiez n'existe plus (vous avez supprimé le dernier enregistrement de la dernière page, par exemple), la liste passe à la dernière page qui existe.
Astuce
Dans les deux modes, utilisez les Filtres (where) pour limiter ce qui est lu. « Tout charger » avec un filtre correct est rapide ; sans aucun filtre, c'est demander la table entière.
Les liaisons — d'où vient une valeur
Chaque fois qu'un filtre, une clé ou une propriété a besoin d'une valeur, vous utilisez la même pièce : la liaison. Le premier sélecteur dit la source ; le reste change selon elle :
| Source | Ce que c'est |
|---|---|
| Fixe | Une valeur saisie sur place, la même pour tout le monde. |
| Param | Un paramètre de la route de l'écran (section Paramètres de route). |
| Session | Un champ de l'utilisateur connecté (userId, username, name). |
| État | Une valeur conservée dans la mémoire de l'app avec keplin.state.set() — disponible sur tous les écrans. |
| Datastore | Un champ d'un autre datastore de l'écran — la base du maître-détail. |
| Widget | La valeur actuelle d'un autre widget de saisie — la base des filtres interactifs. |
Un champ écrit aussi dans l'état : dans la Liaison de données du champ, choisissez État et saisissez la Clé. Un filtre avec la source État et la même clé relit les données quand la valeur change. C'est ainsi qu'une zone de widgets de la barre filtre les pages ; voir Navigation de l'app.
Les sources Datastore et Widget n'existent que dans les datastores à l'intérieur de widgets — elles dépendent du reste de l'écran. Dans les datastores de l'écran, il ne reste que les quatre premières.
Avec ces pièces, on monte les schémas du quotidien sans code :
- Maître-détail — la table d'opportunités du compte : dans le datastore de la
table, filtre
contaId eq→ Datastore ▸conta▸id. Sélectionner un autre compte recharge le détail. - Filtre par texte — un Champ de texte « rechercher » et, dans le datastore
de la table,
nome contains→ Widget ▸ le champ. (Pour ne filtrer qu'au clic sur un bouton, cela se fait par événement — voir Événements et le SDK.)
Relier des champs de formulaire à un enregistrement
Chaque champ de formulaire a, dans la catégorie Données, la section Liaison de données : choisissez le datastore d'enregistrement et le champ. À partir de là, le champ de saisie affiche la valeur chargée et les modifications restent dans le datastore — non enregistrées — jusqu'à ce que quelqu'un enregistre.
L'étape finale est un bouton dont l'événement enregistre :
const ok = await keplin.data.store("conta").save();
if (ok) {
keplin.ui.toast("Enregistré.", "success");
}
Ce code est exactement ce que l'action prédéfinie Enregistrer le datastore de
l'éditeur d'événements insère pour vous. save() valide d'abord (obligatoires,
règles, scripts de validation) et n'enregistre que si tout passe ; il renvoie
true s'il a enregistré.
Quand vous enregistrez un enregistrement qui existait déjà, seuls les champs modifiés depuis la lecture sont envoyés. Un champ vidé est enregistré vide (nul dans la base de données), et dans un champ décimal « 12,5 » se lit comme un nombre.

Avec des modifications non enregistrées, quitter la page demande une
confirmation : par les menus, par le bouton Retour du navigateur ou en
fermant l'onglet. Un modal le demandait déjà à la fermeture. La navigation
faite par code (keplin.nav.go) ne demande rien : qui l'appelle a déjà
décidé, et vient souvent d'enregistrer.
À l'enregistrement d'un nouvel enregistrement, un champ que l'écran n'affiche
pas n'est pas envoyé : dans une colonne avec une valeur par défaut dans la
base, ou générée par elle, cette valeur s'applique ; une colonne obligatoire
sans valeur par défaut doit être à l'écran, et le formulaire dit laquelle
manque. Une clé saisie dans un champ de l'écran (une clé naturelle, comme un
code d'article) est celle d'un nouvel enregistrement ; posée par code avec
set(), elle reste celle d'un enregistrement à modifier. Enregistrer un
enregistrement qui a cessé d'exister entre-temps donne une erreur, et non «
Enregistré ».
La fenêtre Configurer le datastore, champ par champ

| Champ | Enregistrement | Liste |
|---|---|---|
| Nom du datastore | ✓ | ✓ |
| API | ✓ | ✓ |
| Quel enregistrement charger (clé) | ✓ | — |
| Filtres (where) | — | ✓ |
| Chargement / Par page | — | ✓ |
| Charger automatiquement | ✓ | ✓ |
Datastores sur des écrans publics
Sur un écran marqué Écran public (sans session), les données viennent uniquement d'APIs avec lecture publique : le sélecteur ne montre que celles-là, et une API déjà choisie qui ne serait pas publique est signalée — Cette API n'a pas de lecture publique — sur un écran sans session, elle ne charge pas de données. La lecture se marque comme publique dans l'éditeur de l'API.
Les données dans le code
Tout ce que font les datastores est aussi dans le SDK des événements —
keplin.data.store("nom") renvoie le datastore par son nom, avec reload(),
setWhere(), get()/set()/save() et compagnie. Le chapitre
Événements et le SDK le parcourt.
Pourquoi ne… ?
- Pourquoi cela ne charge-t-il pas de données ? Regardez, dans l'ordre : Charger automatiquement est-il activé ? L'API choisie existe-t-elle et est-elle publiée ? Sur un écran public, la lecture de l'API est-elle publique ? Le filtre n'est-il pas en train de tout exclure ?
- Pourquoi cela ouvre-t-il toujours un enregistrement vide ? Le datastore d'enregistrement n'a pas de conditions dans Quel enregistrement charger (clé) — ou le paramètre utilisé dans la condition n'arrive pas dans la route.
- Pourquoi ai-je changé le modèle et la nouvelle colonne n'apparaît pas ? Le datastore conserve un instantané des champs de l'API du moment où vous l'avez choisie. Rouvrez Configurer le datastore et rechoisissez l'API pour actualiser l'instantané. Ce qu'est chaque colonne déjà choisie (type, obligatoire ou non, valeur par défaut, date) se met à jour tout seul à l'ouverture de l'écran ; seules les nouvelles colonnes doivent être choisies.
- Pourquoi la pagination est-elle lente ? Vous êtes en Une page à la fois avec beaucoup d'allers-retours au serveur — ou en Tout charger sans filtres sur une table énorme. Ajustez le mode à la taille réelle des données.