République
française
dsfr-data
Documentation — Web Components DSFR pour la dataviz
Composant invisible de connexion aux données. Récupère des données depuis une API REST (URL brute ou via un adapter specialise) et les distribue aux autres composants via le système d'événements. Supporte aussi les données JSON inline.
Invisible : Ce composant ne rend rien visuellement. Il agit comme un "pont" entre votre API et les composants de visualisation.
dsfr-data-source se connecte a une API, récupère les données et les
redistribue. En mode adapter, il gère la pagination automatique, les filtres server-side
et la pagination pilotable (server-side).
dsfr-data-query est une couche de transformation client-side : il filtre, groupe,
agrège et trie les données. Il ecoute un dsfr-data-source via l'attribut
source et traite les données côté client.
Pattern recommande : <dsfr-data-source api-type="..."> +
<dsfr-data-query source="..."> pour combiner fetch serveur et
transformation client.
Le composant supporte trois modes :
| Mode | Description |
|---|---|
| URL brute | Appel HTTP générique via l'attribut url. Supporte GET/POST, headers,
params JSON, extraction via transform et pagination manuelle via paginate. |
| Adapter | Active via api-type (sans url). L'adapter gère la construction d'URL,
la pagination automatique, les filtres et le parsing spécifiques au provider
(OpenDataSoft, Tabular, Grist, INSEE Melodi). |
| Inline data | Données JSON passees directement via l'attribut data. Pas de fetch HTTP. |
En mode adapter, l'attribut api-type sélectionne le provider :
| api-type | API | Identifiant requis | Pagination | Max records |
|---|---|---|---|---|
opendatasoft |
OpenDataSoft (v2.1) | dataset-id |
offset, 100/page, max 10 pages | 1 000 (relevable : max-records) |
tabular |
Tabular API (data.gouv.fr) | resource |
page, 200/page, max 125 pages | 25 000 (relevable : max-records) |
grist |
Grist (grist.numerique.gouv.fr, getgrist.com) | base-url |
offset, 100/page, illimitee | Illimite |
insee |
INSEE Melodi (api.insee.fr/melodi) | dataset-id |
page, 1 000/page, max 100 pages | 100 000 |
| Attribut | Type | Format / Valeurs | Description |
|---|---|---|---|
id | String | id="ma-source" | Identifiant unique (requis). Les composants en aval l'utilisent pour s'abonner aux données. |
refresh | Number | Entier en secondes. 0 = off | Rafraîchissement automatique en secondes (0 = désactivé). |
transform | String | "results", "data.items" | Chemin JSONPath vers le tableau de données dans la réponse. Ex: "results", "data.items". |
headers | String | '{"apikey":"abc123"}' | En-têtes HTTP en JSON. Ex: '{"Authorization": "Bearer xxx"}'. Quand le fournisseur attend sa clé sous un en-tête précis (déclaré par sa configuration), un apikey nu est réécrit automatiquement au bon format (#655) ; détail par fournisseur : table des capacités d'ARCHITECTURE. |
api-key-ref | String | "tmdb" | Référence vers une clé API déclarée dans window.DSFR_DATA_KEYS |
cache-ttl | Number | Entier en secondes. Défaut : 3600 | TTL du cache externe en secondes (0 = desactive). Actif uniquement si la page hote enregistre window.DSFR_DATA_CACHE_PROVIDER (#307) — no-op en embed anonyme. C'est un repli hors ligne, côté navigateur : il n'a AUCUN rapport avec le relais (relay-url), ne lui est pas transmis et ne règle pas la durée de son cache. |
| Attribut | Type | Format / Valeurs | Description |
|---|---|---|---|
url | String | URL relative ou absolue | URL de l'API a interroger (mode URL brute). Vide en mode adapter ou en mode data inline. |
method | GET · POST | GET | POST | Méthode HTTP : GET (défaut) ou POST. |
params | String | '{"limit": 100}' | Paramètres de requête en JSON. Mode URL : query string en GET, corps de la requête en POST. **Mode adaptateur** (#726) : les paires sont ajoutées à l'URL construite par l'adaptateur, ce qui sert les paramètres propres à l'API que la bibliothèque ne modélise pas — par exemple params='{"timezone":"Europe/Paris"}' pour lire des dates dans un fuseau donné, sans quitter le mode adaptateur. Les clés que l'adaptateur construit lui-même (il les déclare : clauses, pagination, projection, suffixes d'opérateur) sont réservées : elles sont refusées avec une erreur de configuration plutôt que d'écraser une clause (#1137). Seuls les adaptateurs qui acceptent des paramètres libres les transmettent (en chargement paginé comme en fetch-mode="export") ; les autres les ignorent — voir la table des capacités d'ARCHITECTURE. |
paginate | Boolean | Presence = true | Active la pagination serveur en mode URL : injecte page/page_size dans l'URL et publie la meta. La source ne livre alors qu'UNE page. Une dsfr-data-query en aval qui regroupe ou agrège (group-by, aggregate, explode) calcule sur cette seule page : le chiffre est partiel. Le mode URL ne délègue rien et aucun attribut ne lui fait charger le jeu entier — sans paginate, c'est la page par défaut de l'API qui revient. La requête le dit donc sans se refuser (#1242) : avertissement en console, réserve au volet Diagnostic. Pour un chiffre sur tout le jeu, passer par un api-type qui sait le charger, ou par une URL qui rend déjà l'agrégat. L'ORDRE des lignes est celui de l'API, et la source ne peut rien en garantir (#1279) : elle ne connaît ni l'API ni ses paramètres de tri, et ne lit qu'une page à la fois. Une API qui pagine sans ordre total peut rendre une ligne sur deux pages et n'en jamais servir une autre. C'est à l'URL de porter un tri sur une colonne UNIQUE (ou complété par une colonne unique) — le paramètre de tri de l'API, écrit dans url ou dans params. |
page-size | Number | Entier positif. Défaut : 20 | Taille de page pour la pagination serveur (nombre de records par page). |
use-proxy | Boolean | Presence = true | Force le passage par le proxy CORS générique (pour les APIs externes sans CORS). Ne vaut qu'en mode URL (url="…") : avec un api-type, l'attribut est sans effet sur un hôte que le proxy ne relaie pas par un endpoint dédié (un portail Opendatasoft, par exemple) — la requête part en direct, et la source le signale une fois en console. Sans effet sur une requête qui part au relais (relay-url) : le relais prend toutes les requêtes GET vers une autre origine, use-proxy ne garde que ce que le relais ne porte pas (requêtes POST, cible hors https). |
proxy-url | String | "" | Domaine du proxy CORS pour CETTE source (#340), prioritaire sur window.DSFR_DATA_PROXY et la config build-time. Sert à la fois la réécriture des hôtes connus et le use-proxy générique. Vide = résolution proxy globale habituelle. Ex: proxy-url="https://mon-proxy.fr". Seuls les hôtes connus sont relayés (Tabular, Grist gouv et SaaS, Albert, INSEE Melodi). Sur tout autre hôte — un portail Opendatasoft en mode adaptateur, ou une URL quelconque sans use-proxy — l'attribut est SANS EFFET : la requête part en direct vers l'API. La source l'écrit alors une fois en console (« proxy-url est sans effet »), et le volet Diagnostic le reprend. Pour faire passer un portail Opendatasoft (ou tout autre hôte) par le domaine du site, c'est relay-url qu'il faut poser : proxy-url désigne un autre contrat (endpoints dédiés et /cors-proxy, toutes méthodes, en-têtes transmis). Avec un relais, proxy-url ne sert plus que les requêtes que le relais ne porte pas (POST du mode SQL de Grist). |
| Attribut | Type | Format / Valeurs | Description |
|---|---|---|---|
api-type | String | opendatasoft | tabular | grist | insee | Type d'API : l'identifiant d'un adaptateur du registre (ceux de la bibliothèque, ou un adaptateur ajouté par registerAdapter). Toute autre valeur que generic active le mode adaptateur ; generic avec une url reste en mode URL. |
base-url | String | "https://data.iledefrance.fr" | URL de base de l'API, pour les adaptateurs qui adressent un portail par son URL. |
dataset-id | String | "mon-dataset-2024" | Identifiant du jeu de données, pour les adaptateurs qui désignent un jeu par son identifiant. |
resource | String | "2876a346-d50c-..." (UUID) | Identifiant de la ressource (fichier d'un jeu), pour les adaptateurs qui désignent une ressource. |
where | String | Colon : "champ:op:val"ODS : ODSQL libre | Clause WHERE statique, déléguée à l'API de l'adaptateur. Une liste in dont une valeur porte une parenthèse ou une virgule est déléguée comme les autres (#1233) — notin, sur Tabular, ne l'est plus du tout : il est calculé dans le navigateur (#1312, voir plus bas). Sur l'API Tabular, qui écarte sans erreur une telle valeur écrite nue, l'adaptateur l'envoie entre guillemets — seule forme que l'API lise —, en chargement complet comme en pagination serveur ; les autres valeurs de la liste restent nues. Cette forme n'est écrite dans aucune documentation de l'API. Si elle est refusée, l'adaptateur se replie, et le volet Diagnostic le signale : - en chargement complet, il calcule la clause lui-même, sur les lignes chargées. Le résultat reste juste ; les autres clauses restent déléguées, mais toutes les lignes qu'elles gardent sont chargées (une requête par page, sous max-records), et un group-by posé à côté n'est plus délégué — les lignes filtrées sont rendues brutes, une dsfr-data-query en aval regroupe ; - en pagination serveur (server-side), où ce calcul n'est pas possible, la liste repart sans guillemets : la valeur est écartée par l'API et le résultat est incomplet. CLAUSE CALCULÉE PAR LA SOURCE (#1285, ADR-159). Un filtre n'est confié au serveur que si la STRUCTURE de la comparaison y est la même que dans le navigateur. contains et notcontains sont une sous-chaîne ; le like d'un portail Opendatasoft compare des mots (like "%aliment%" rend 0 ligne pour 14 366 « alimentation », mesuré). Sur Opendatasoft, une telle clause — venue d'un filtre de dsfr-data-context (dont dsfr-data-search en mode context) ou du where d'une dsfr-data-query seule lectrice de la source — n'est donc jamais confiée au portail : c'est la source qui la calcule, sur les lignes que le reste du filtre a laissées passer. Sur Tabular et Grist (#1285, lot 2), contains reste confié à leur LIKE SQL, jokers de la valeur échappés ; notcontains (leur NOT LIKE écarterait les valeurs absentes) et, sur Tabular, un contains dont la valeur porte * et notin (son not.in écarterait aussi les absents, #1312) sont calculés par la source. Cet attribut-ci suit la même règle quand il est écrit en grammaire colon (Tabular, Grist) ; sur Opendatasoft il est écrit en ODSQL et part tel quel (y écrire un like reste possible, avec sa sémantique de mots). Le résultat est juste, au prix d'un chargement plus large : toutes les lignes des autres clauses, sous max-records (au-delà, le filtre ne porte que sur les lignes lues, et la source le signale comme une troncature). Deux montages ne lui livrent pas le jeu et passent en ERREUR DE CONFIGURATION, qui nomme la clause et la correction : server-side (une page), et group-by, aggregate ou un select qui calcule posés sur la source (le serveur agrégerait des lignes non filtrées — porter le calcul par une dsfr-data-query en aval). |
select | String | "sum(pop) as total, region" | Clause SELECT, liste séparée par des virgules. Sa grammaire dépend de l'adaptateur (table des capacités d'ARCHITECTURE, ligne « projection select ») : clause complète ou simple liste de noms de colonnes ; un adaptateur sans projection l'ignore. **Clause complète** : select="count(*) as total, region". Une expression (fonction, alias as, *, chemin pointé, opérateur) est transmise telle quelle ; un nom de champ qui n'est pas un identifiant nu (espace, accent, chiffre initial comme 1_uai) est échappé automatiquement (#767). Une virgule à l'intérieur d'une fonction ou d'une chaîne ne sépare pas. Un select fait UNIQUEMENT d'agrégats, sans group-by (select="sum(montant) as total") se charge en une requête d'une ligne, la valeur calculée par le serveur sur tout le jeu (#810) ; si le filtre ne garde aucune ligne, un count vaut 0 et les autres fonctions null. Quand une dsfr-data-query délègue son regroupement à cette source, le select émis est COMPOSÉ depuis l'aggregate de la query (colonnes d'agrégat + colonnes du group-by) : ce select ne l'écrase pas, sinon la colonne d'alias n'existerait pas dans la réponse et le chiffre affiché serait faux (#859). S'il définit une colonne par une expression aliasée (year(date) as annee) que le regroupement vise, la délégation est refusée — avertissement en console, regroupement calculé côté client. **Liste de noms de colonnes** (projection seule, #985) : select="nom, Code sexe", espaces et accents admis — l'API ne rend que ces colonnes, soit dix fois moins d'octets sur un jeu large. Aucune colonne n'est ajoutée d'office : une colonne lue en aval (graphique, liste, facette, filtre client) doit y figurer, et un nom inconnu du jeu fait répondre l'API en erreur. Sans effet quand un group-by ou un aggregate est posé (sur la source ou délégué par une query), si l'API refuse la projection à côté d'un agrégateur. Une expression (fonction, alias, *) est ignorée avec un avertissement : toutes les colonnes sont chargées. |
group-by | String | "champ1, champ2" | Group-by, délégué aux adaptateurs déclarant serverGroupBy. Avec un select en clause complète, un élément peut être une expression aliasée, avec ou sans fonction (year(date) as annee, periode as an), transmise telle quelle — l'alias as y est obligatoire (#641). Même découpe et même échappement que select (#767) : date_format(d, 'yyyy-MM') as m reste d'un seul tenant. |
aggregate | String | "champ:fn" ou "champ:fn:alias" | Agrégation, déléguée aux adaptateurs déclarant serverGroupBy. |
order-by | String | "champ:asc" ou "champ:desc" | Tri (champ:asc, champ2:desc), délégué aux adaptateurs déclarant serverOrderBy. Une API qui pagine par décalage et ne trie que sur une clé (Tabular) rend un ordre instable d'une page à l'autre dès que la clé n'est pas unique : des lignes reviennent deux fois, d'autres jamais, pour un compte juste. Un tri délégué qui s'étend sur plusieurs pages est donc rendu sûr par l'adaptateur (#1202, #1233), lignes brutes comme groupes d'un group-by : - tout le jeu est chargé : il est relu sans tri et trié sur place (une requête de plus), dans l'ordre du pipeline — vides, puis nombres, puis textes —, le même que celui d'une dsfr-data-query ; - limit ou max-records coupe le chargement : le tri reste au serveur, complété d'une clé de départage (l'identifiant de ligne, ou les autres colonnes du group-by) qui le rend total. Un chargement d'une seule page, et un regroupement trié sur sa seule colonne de regroupement, gardent le tri du serveur tel quel. Sur Opendatasoft (#1274), la pagination par décalage n'a AUCUN ordre garanti : le défaut touche aussi un chargement sans order-by. Dès qu'un chargement dépasse une page (100 lignes), l'adaptateur relit donc sur un ordre total — le tri demandé, inchangé et en tête, complété de toutes les colonnes triables du jeu (texte, nombre, date, booléen, dans l'ordre de ses champs ; pour des groupes triés, les autres clés du group-by). Coût : deux requêtes de plus, la première page relue et les métadonnées du jeu, lues une fois. Sans order-by, les lignes d'un chargement de plus d'une page arrivent alors triées sur les colonnes du jeu, et non plus dans l'ordre, arbitraire, de l'API. Un chargement d'une seule page ne change pas : ni tri ajouté, ni requête de plus. Si l'API refuse ce tri, ou si les colonnes du jeu sont illisibles, la source charge comme avant et le volet Diagnostic signale une pagination sans ordre garanti. |
limit | Number | Entier positif. 0 = illimite | Limite du nombre de résultats |
max-records | Number | Entier positif. 0 = plafond par défaut de l'adapter | Plafond de lignes du chargement complet en mode adaptateur (#233, #1027), honoré par les adaptateurs qui paginent eux-mêmes leur chargement complet. 0 = plafond par défaut de l'adaptateur (valeurs par adaptateur : table des capacités d'ARCHITECTURE, ligne « plafond fetchAll »). À relever explicitement pour charger un jeu plus long par la pagination — par exemple une carte des ≈ 35 000 communes (max-records="40000") — ou pour les tableaux de bord « un fetch, N agrégations client » : attention au nombre de requêtes en boucle (une par page de l'API) et au poids mémoire. Un limit plus petit reste prioritaire. Quand le plafond coupe le jeu, la source signale la troncature (truncated) et un avertissement console cite max-records. Un chargement coupé par le plafond et trié (order-by) rend les premières lignes du tri, chacune une fois : sur Tabular, le tri délégué est complété d'une clé de départage (#1233). Si l'API la refuse, le tri du serveur est gardé tel quel et le volet Diagnostic signale un tri instable — des lignes à valeurs égales peuvent alors manquer ou être doublées aux limites de page. Sur Opendatasoft (#1274), un chargement coupé rend de même les premières lignes d'un ordre total, avec ou sans order-by : le tri demandé puis toutes les colonnes triables du jeu (voir order-by). L'API n'y sert pas plus de 10 000 lignes par la pagination, quel que soit ce plafond : au-delà, fetch-mode="export". |
server-side | Boolean | Presence = true | Mode pagination serveur (datalist, tableaux). Ce qui est délégué ne change pas avec ce mode : une page porte les mêmes filtres, le même regroupement et les mêmes agrégats qu'un chargement complet — seule la façon dont les lignes arrivent change (#852). La source ne livre qu'UNE page : rien de ce qui se calcule côté client sur l'ensemble des lignes n'a de sens derrière elle. Une dsfr-data-query en aval dont le regroupement ou l'agrégat n'est pas délégué — part ou cumul, explode, agrégat sans group-by, fonction que l'adaptateur ne traduit pas, transformateur amont qui change les colonnes, source lue par d'autres composants, source déjà regroupée — passe en **erreur de configuration** au lieu d'émettre un chiffre partiel (#1242). Deux corrections : - retirer server-side : la source charge le jeu entier, dans la limite de max-records ; - si le jeu dépasse ce plafond, ou si une liste paginée lit la même source : donner à la requête sa propre source sans server-side, qui porte le regroupement délégable (group-by, aggregate) — le serveur regroupe alors le jeu entier, et la part ou le cumul se calcule en aval, sur les groupes. Une requête qui délègue réellement son regroupement, ou qui ne regroupe pas (filtre et tri d'un tableau paginé), n'est pas concernée. Les pages sont servies une à une et le jeu n'est jamais relu : seul le serveur peut tenir l'ordre d'une page à l'autre, faute de quoi un usager qui feuillette voit des lignes en double et n'en voit jamais d'autres. Chaque page est donc demandée sur un ordre total (#1274, #1233) : sur Opendatasoft, le tri de la liste (ou aucun) complété de toutes les colonnes triables du jeu, lues une fois dans ses métadonnées ; sur Tabular, le tri demandé complété de l'identifiant de ligne — sans tri, l'API suit déjà cet identifiant, rien n'est ajouté. Sur un très gros jeu sans filtre, ce tri allonge la réponse de la première page (mesuré sur 1,7 million de lignes : 5,5 s à froid, 0,7 s ensuite, contre 0,2 s sans tri) : un order-by sur une colonne unique ne coûte pas moins, un where qui réduit le jeu, si. Si l'API refuse la clé de départage, la page est servie comme avant et le volet Diagnostic le signale. |
max-records
En mode adaptateur, le chargement complet s'arrête à un plafond de sécurité : 1 000 lignes sur
Opendatasoft, 25 000 sur Tabular (125 pages de 200). max-records le relève
explicitement, et vaut pour les deux adaptateurs : Opendatasoft (#233) et Tabular (#1027). Sur
Tabular, une carte de toutes les communes (environ 35 000 lignes) demande
max-records="40000", soit jusqu'à 200 requêtes de 200 lignes : c'est le prix à
peser, en requêtes comme en mémoire. Un limit plus petit reste prioritaire.
<dsfr-data-source id="communes" api-type="tabular" resource="…" max-records="40000"> </dsfr-data-source>
Quand le plafond coupe le jeu, la troncature n'est pas muette : la source la signale
(truncated, lisible dans le volet Diagnostic) et un avertissement console cite
max-records comme moyen de la lever. C'est vrai aussi sur un group-by
délégué, dont l'API ne donne pas le total : une page pleine au plafond avec une page suivante
annoncée suffit à la signaler. Les autres adaptateurs (Grist, INSEE Melodi) ignorent l'attribut.
Une API qui pagine par décalage ne rend chaque ligne une seule fois que si l'ordre est le même d'une page à l'autre. Sinon des lignes reviennent deux fois, d'autres jamais, et le nombre de lignes reste juste : rien ne le signale, et un sous-total, un cumul ou un historique sont faux. La source s'en charge, sans rien à écrire dans la page (#1202, #1233, #1274).
| Chargement | Opendatasoft | Tabular |
|---|---|---|
| Une seule page (100 lignes, 200 sur Tabular) | Rien ne change : ni tri ajouté, ni requête de plus. | Rien ne change. |
| Plusieurs pages, jeu complet | La première page sert de sonde ; le jeu est relu trié sur order-by puis sur toutes ses colonnes triables. Deux requêtes de plus. | Avec order-by : relu sans tri, trié sur place (une requête de plus). Sans : inchangé. |
Coupé par limit ou max-records | Même ordre total : les premières lignes de order-by puis des colonnes du jeu. | Avec order-by : tri complété de l'identifiant de ligne. |
server-side | Chaque page part sur l'ordre total. | Avec un tri : complété de l'identifiant de ligne. Sans : inchangé. |
fetch-mode="export" | Une requête : non concerné. | Non concerné. |
Sur Opendatasoft, l'API ne garantit aucun ordre sans tri : le défaut touche aussi
une source sans order-by, et pas à chaque chargement (mesuré sur un jeu de 321
lignes : 297 distinctes dix fois sur dix un jour, 321 cinq fois sur cinq un autre).
Conséquence visible : sans order-by, les lignes d'un jeu de plus de 100 lignes
arrivent triées sur les colonnes du jeu, dans l'ordre de ses champs, et non plus dans l'ordre
de l'API. Pour un autre ordre, poser order-by ; un cumul calculé en aval sans tri
suit l'ordre livré.
Les colonnes retenues sont celles que l'API sait trier (texte, nombre, date, booléen), lues une
fois dans les métadonnées du jeu ; pour des groupes triés, ce sont les autres clés du
group-by. Sur un jeu de plus d'un million de lignes sans where, ce tri
allonge la première réponse de quelques secondes. Si l'API refuse la clé de départage, la source
charge comme avant et le volet Diagnostic le signale (« pagination sans ordre garanti » sur
Opendatasoft, « tri serveur instable » sur Tabular) : poser alors un order-by sur
une colonne unique, ou charger en une requête avec fetch-mode="export".
Grist. Un chargement complet tient en une requête : rien à paginer. En
server-side, l'API /records ne sait pas sauter de lignes — son
paramètre offset est ignoré, sans erreur. La source demande donc la page N par
limit = N × page-size et n'en garde que la dernière tranche : chaque page est
juste, et la page N coûte N pages de transfert. Un order-by paginé est complété
par l'identifiant de ligne de Grist (id), pour que les ex-æquo ne changent pas
de page. Avec un where à opérateur avancé, un group-by ou un
aggregate, la requête passe par l'endpoint SQL du document, paginé sur le même
ordre total.
INSEE Melodi. Au-delà de 1 000 observations, la source pagine par numéro de
page, et l'API n'offre aucun paramètre de tri. L'ordre de ses observations a été mesuré stable
d'une page à l'autre ; rien ne le garantit, et la source ne peut pas l'imposer. Elle vérifie
donc, une fois toutes les pages reçues, qu'aucune observation n'est revenue deux fois — une
observation s'identifie par ses dimensions. Si c'est le cas, le volet Diagnostic affiche
« lignes revenues d'une page à l'autre » : autant d'observations manquent, alors que le compte
reste juste. Resserrer le where pour que le chargement tienne en une page, ou
recharger.
fetch-mode
En mode adaptateur, la source pagine par tranches de 100 lignes. Pour une page « un fetch, N
agrégations client », c'est autant d'allers-retours ; et sur un group-by à beaucoup
de groupes, le portail ne rend qu'une page de groupes.
fetch-mode="export" passe par l'endpoint d'export du portail, qui rend tout d'un coup,
avec les mêmes clauses (select, where, group-by,
order-by).
| Attribut | Type | Défaut | Description |
|---|---|---|---|
fetch-mode | records · export | records | Stratégie de chargement en mode adaptateur (#689) : records (défaut, comportement historique — pagination par pages) ou export, qui charge tout le jeu en **une seule requête**, ou en quelques plages, sur l'endpoint d'export de l'API. Implémenté par les adaptateurs qui ont un endpoint d'export (table des capacités d'ARCHITECTURE, ligne « chargement en une requête ») ; les autres ignorent l'attribut. Selon l'adaptateur, l'export porte les mêmes clauses (select, where, group-by, order-by) ou ne rend que des **lignes brutes** (#1055) : dans ce dernier cas, avec un where, group-by, aggregate ou order-by délégué (posé sur la source ou transmis par une dsfr-data-query), la source reste sur la pagination, qui les exécute côté serveur, et le dit en console. Un export binaire (colonnes projetées depuis select) charge son lecteur à la demande seulement. Pour une première page rapide sur un petit jeu, la pagination reste plus vive ; l'export l'emporte au-delà de 1 000 à 2 000 lignes. À activer pour une page « un fetch, N agrégations client », un jeu de plus de 1 000 lignes, ou un group-by à beaucoup de groupes : l'API les rend tous d'un coup au lieu d'une page. À ne pas activer avec server-side (pagination page par page), qui reste sur l'endpoint paginé et signale la contradiction dans la console. Une seule requête n'a pas d'ordre à tenir d'une page à l'autre : le mode export n'est pas concerné par la relecture sur un ordre total que la pagination impose au-delà d'une page (#1274, voir order-by), et les lignes y arrivent dans l'ordre de l'API, ou dans celui d'order-by. En mode export le total serveur est inconnu : la troncature est détectée en demandant une ligne de plus que le plafond max-records, qui borne aussi les lignes lues. Si l'API n'expose pas d'endpoint d'export, la source retombe une fois sur le chargement paginé, avec un avertissement en console. |
<dsfr-data-source id="src" api-type="opendatasoft" base-url="https://data.economie.gouv.fr" dataset-id="mon-jeu" fetch-mode="export" max-records="20000"> </dsfr-data-source>
Implémenté par les adaptateurs OpenDataSoft et Tabular ; les autres l'ignorent, et une source dont le
portail n'expose pas d'endpoint d'export retombe une fois sur le chargement paginé, avec un
avertissement console. À ne pas combiner avec server-side, qui pagine page par page
et reste sur l'endpoint paginé : la contradiction est signalée dans la console. Sur OpenDataSoft, le
total serveur est inconnu en mode export, donc la troncature est détectée en demandant une ligne de
plus que le plafond max-records — pensez à relever ce plafond en même temps.
Sur une source api-type="tabular", fetch-mode="export" lit l'export
Parquet que data.gouv publie pour chaque ressource, par plages, en ne lisant que les
colonnes du select. Mesuré sur le jeu IRVE (223 174 lignes) : le jeu entier en 0,66 s et
17 requêtes, là où la pagination met 10,8 s et 125 requêtes pour 25 000 lignes. Le lecteur
(≈ 22 Ko gzip) n'est chargé qu'à ce moment-là : il n'alourdit pas les autres pages.
where, un group-by, un
aggregate ou un order-by délégué — posé sur la source ou transmis par une
dsfr-data-query —, la source reste sur la pagination, qui les exécute côté serveur, et
le dit une fois en console. Pour « un fetch, N agrégations », posez les calculs sur des
dsfr-data-query qui partagent la source : ils restent alors côté client.
max-records (25 000 par défaut, comme la pagination) borne les lignes
lues. Le fichier annonce son nombre de lignes : le total est connu, et une troncature se dit
comme en pagination.
AAAA-MM-JJ, colonne __id quand aucun select n'est posé. Une
ressource sans export retombe sur la pagination, avec un avertissement.
<dsfr-data-source id="bornes" api-type="tabular" resource="eb76d20a-8501-400e-b336-d85724de5435" select="nom_station, consolidated_latitude, consolidated_longitude" fetch-mode="export" max-records="250000"> </dsfr-data-source>
| Attribut | Type | Format / Valeurs | Description |
|---|---|---|---|
data | String | JSON valide (tableau ou objet) | Données JSON inline (pas de fetch) |
Sans précaution, une source interroge son API dès le montage et rapatrie le jeu entier — une
requête coûteuse dont personne ne regarde le résultat sur une page d'exploration, où l'utilisateur
va de toute façon commencer par choisir une commune, une année, un thème.
require-where retarde le premier appel jusqu'à ce filtre.
| Attribut | Type | Défaut | Description |
|---|---|---|---|
require-where | Boolean | false | Ne rien charger tant qu'aucun filtre n'a été reçu (#690). Pensé pour les pages d'exploration : sans cet attribut, une source interroge l'API dès le montage et rapatrie le jeu entier — une requête coûteuse dont personne ne regarde le résultat. Avec lui, la source reste en attente, émet dsfr-data-idle et ne part chercher les données qu'au premier filtre. Ce qui compte comme filtre : les clauses reçues par commande — facettes, recherche, dsfr-data-context, délégation d'un dsfr-data-query (son where, avec ou sans group-by, quand elle est seule lectrice de la chaîne — #856). Le where STATIQUE de la source ne compte PAS : il fait partie de la définition du jeu, pas du geste de l'utilisateur ; le contraire rendrait l'attribut sans effet sur toute source qui restreint déjà son périmètre. Quand le dernier filtre est retiré, la source repasse en attente : jamais de requête « tout » implicite. Sans effet en mode données inline (data), qui ne fait aucune requête. |
<dsfr-data-source id="src" api-type="opendatasoft" require-where base-url="https://data.economie.gouv.fr" dataset-id="equipements"> </dsfr-data-source> <dsfr-data-facets source="src" fields="commune"></dsfr-data-facets> <dsfr-data-list source="src" idle-message="Choisissez une commune pour afficher ses équipements"> </dsfr-data-list>
Ce qui compte comme filtre : les clauses reçues par commande — facettes,
recherche, dsfr-data-context, délégation d'un
dsfr-data-query. Le where STATIQUE de la source ne
compte pas : il fait partie de la définition du jeu, pas du geste de l'utilisateur — le contraire
rendrait l'attribut sans effet sur toute source qui restreint déjà son périmètre. Quand le dernier
filtre est retiré, la source repasse en attente : jamais de requête « tout » implicite. Sans effet
en mode data inline, qui ne fait aucune requête.
Pendant l'attente, la source publie dsfr-data-idle plutôt que des données vides. Les
afficheurs en aval (list,
display, kpi,
chart, podium,
search, a11y) rendent alors
leur idle-message — « Choisissez un filtre pour afficher les données » par défaut —
et non « aucune donnée », qui annoncerait une requête revenue vide. Un pendant purement client
existe sur dsfr-data-query.
Une page à onglets déclare ses sources pour tous les panneaux ; cinq sur six sont
fermés à l'arrivée, et pourtant toutes les requêtes partent au chargement. Avec
lazy, la première requête attend qu'un consommateur de la source entre dans une marge
de 200 px autour du viewport. Un panneau fermé est en display:none : il n'a pas de
boîte, il n'intersecte jamais, et l'observateur se déclenche à l'ouverture de l'onglet.
| Attribut | Type | Défaut | Description |
|---|---|---|---|
lazy | Boolean | false | Ne rien charger tant que personne ne regarde (#931, AM-083). Une page à onglets déclare ses sources pour TOUS les panneaux ; cinq sur six sont fermés à l'arrivée, et pourtant toutes les requêtes partent au chargement. Avec lazy, la première requête attend qu'un consommateur de cette source entre dans une marge de 200 px autour du viewport (IntersectionObserver, la même marge que dsfr-data-map et que le lazy de dsfr-data-repeat, #891). Un panneau d'onglet fermé est en display:none : il n'a pas de boîte, il n'intersecte donc jamais, et l'observateur se déclenche à l'ouverture de l'onglet. **Ce qui est observé** : les FEUILLES de la chaîne aval (chart, list, kpi, display, podium, a11y, repeat ; pour une couche de carte, la carte qui la porte), suivies à travers les transformateurs — un dsfr-data-query est un tuyau déclaré en haut de page, l'observer reviendrait à ne rien différer. lazy-target remplace cette détection par un sélecteur explicite. **Opt-in strict** : sans l'attribut, la source part au chargement, exactement comme avant. **Dégradations, toutes du côté « on charge » :** sans IntersectionObserver, la source part immédiatement ; si la page ne déclare AUCUN consommateur (ou si lazy-target ne désigne rien), la source part immédiatement et le dit en console — une source qui ne chargerait jamais serait pire que le trafic qu'on cherche à éviter. **Ce que lazy ne promet pas** : un IntersectionObserver n'est pas continu. Il échantillonne aux temps de rendu ; un défilement par crans rapides (barre de défilement jetée, scrollIntoView enchaînés) peut traverser un consommateur sans jamais le rapporter comme visible — la source reste alors en attente jusqu'au prochain passage. C'est le comportement du navigateur, pas un bug de la bibliothèque. Se cumule avec require-where : les deux portes doivent s'ouvrir, et require-where est évalué en premier (c'est son message d'attente que l'utilisateur doit lire). Sans effet en mode données inline (data), qui ne fait aucune requête. |
lazy-target | String | "" | Sélecteur CSS de l'élément dont la visibilité déclenche le chargement, à la place des consommateurs détectés (#931). Sans effet sans lazy. lazy lazy-target="#panneau-2" : la source part quand le panneau entre dans la marge de 200 px. À utiliser quand la détection automatique ne peut pas voir le bon élément — un consommateur créé en JavaScript, une carte dont on préfère observer la section entière, ou plusieurs blocs qu'on veut traiter comme un seul (le sélecteur peut désigner plusieurs éléments : le PREMIER vu ouvre la porte). Sélecteur invalide, ou qui ne désigne aucun élément : la source part immédiatement, avec un message en console. Rien de silencieux. |
<!-- Les sources de l'onglet 2 ne partent qu'à son ouverture --> <dsfr-data-source id="s-clubs" api-type="opendatasoft" lazy base-url="https://data.sports.gouv.fr" dataset-id="clubs"> </dsfr-data-source> <div class="fr-tabs__panel" id="panneau-2"> <dsfr-data-chart source="s-clubs" type="bar" label-field="dep" value-field="n"></dsfr-data-chart> </div> <!-- Cible explicite, quand la détection automatique ne voit pas le bon élément --> <dsfr-data-source id="s-carte" api-type="opendatasoft" lazy lazy-target="#panneau-3" base-url="https://data.sports.gouv.fr" dataset-id="equipements"> </dsfr-data-source>
Ce qui est observé : les feuilles de la chaîne aval (chart, list, kpi,
display, podium, a11y, repeat ; pour une couche de carte, la carte qui la porte), suivies à travers
les transformateurs. Un dsfr-data-query est un tuyau déclaré en haut de page :
l'observer reviendrait à ne rien différer.
Toutes les dégradations vont du côté « on charge » : sans
IntersectionObserver, ou si la page ne déclare aucun consommateur (ou si
lazy-target ne désigne rien), la source part immédiatement et le dit en console. Une
source qui ne chargerait jamais serait pire que le trafic qu'on cherche à éviter.
Ce que lazy ne promet pas : un IntersectionObserver
n'est pas continu. Il échantillonne aux temps de rendu ; un défilement par crans rapides peut
traverser un consommateur sans jamais le rapporter comme visible — la source reste alors en
attente jusqu'au prochain passage. C'est le comportement du navigateur, pas un bug.
Se cumule avec require-where : les deux portes doivent s'ouvrir, et
require-where est évalué en premier — c'est son message d'attente que l'utilisateur
doit lire. Pendant l'attente de lazy, la source publie dsfr-data-idle
avec la raison lazy, que le volet Diagnostic distingue de l'attente d'un filtre.
L'attribut where definit un filtre permanent sur la source.
En mode Tabular / Grist, format colon champ:operateur:valeur :
where="population:gt:5000" where="population:gt:5000, region:in:IDF|OCC|BRE" where="email:isnull"
En mode OpenDataSoft, syntaxe ODSQL libre :
where="population > 5000 AND status = 'active'"
Les composants en aval (dsfr-data-facets, dsfr-data-search) peuvent ajouter
des filtres dynamiques via le système de commandes. Ces filtres sont fusionnes
avec le where statique à chaque requête.
Chaque composant envoie un overlay identifie par une clé unique (whereKey).
Les overlays sont combinés en ET logique. La méthode getEffectiveWhere()
retourne la clause WHERE fusionnee.
<!-- WHERE statique + overlays dynamiques des facettes et recherche --> <dsfr-data-source id="src" api-type="opendatasoft" dataset-id="rappelconso-v2-gtin-trie" base-url="https://data.economie.gouv.fr" where="categorie_produit = 'Alimentation'" server-side page-size="20"> </dsfr-data-source> <!-- La recherche ajoute un overlay WHERE via server-search --> <dsfr-data-search id="search" source="src" server-search> </dsfr-data-search> <!-- Les facettes ajoutent des overlays WHERE --> <dsfr-data-facets id="facets" source="src" fields="categorie_produit, marque_produit" server-facets> </dsfr-data-facets>
Avec server-side, dsfr-data-source charge une seule page
a la fois et ecoute les commandes des composants en aval pour naviguer entre les pages.
Cela permet d'afficher des datasets de milliers d'enregistrements sans tout charger en memoire.
Le composant reagit a trois types de commandes :
| Commande | Source | Effet |
|---|---|---|
{ page } | dsfr-data-list | Change la page courante et re-fetche |
{ where, whereKey } | dsfr-data-facets, dsfr-data-search | Ajoute/supprime un overlay WHERE et reset a la page 1 |
{ orderBy } | dsfr-data-list | Change le tri et re-fetche |
Une source en server-side ne livre qu'une page. Tout ce qui se
calcule côté client sur l'ensemble des lignes n'a donc pas de sens derrière elle. Une
dsfr-data-query en aval dont le regroupement ou l'agrégat n'est pas délégué au
serveur passe en erreur de configuration : elle porte l'attribut
data-dsfr-config-error, les blocs en aval affichent leur état d'erreur, et aucun
chiffre partiel n'est émis. Sont concernés une part ou un cumul (share,
share_percent, running_sum, diff), un
explode, un agrégat sans group-by, une fonction que l'adaptateur ne
traduit pas, un regroupement posé derrière un transformateur qui change les colonnes
(compute, renommage, dépliage), une source lue par d'autres composants, une
source déjà regroupée.
Le message d'erreur nomme la source et les deux corrections :
server-side : la source charge le jeu entier, dans la
limite de max-records ;
server-side, qui
porte le regroupement délégable. Le serveur regroupe alors le jeu entier, et la part ou le
cumul se calcule en aval, sur les groupes.
<!-- le tableau garde sa source paginée --> <dsfr-data-source id="lignes" api-type="tabular" resource="…" server-side page-size="20"></dsfr-data-source> <dsfr-data-list source="lignes" columns="region:Région, population:Population"></dsfr-data-list> <!-- la répartition a la sienne : le serveur regroupe, la part se calcule sur les groupes --> <dsfr-data-source id="par-region" api-type="tabular" resource="…" group-by="region" aggregate="population:sum"></dsfr-data-source> <dsfr-data-query id="parts" source="par-region" aggregate="population__sum:share_percent:part"></dsfr-data-query>
Une requête qui délègue réellement son regroupement, ou qui ne regroupe pas (filtre et tri d'un
tableau paginé), n'est pas concernée. En mode URL, paginate ne livre lui aussi
qu'une page, mais aucun attribut ne lui fait charger le jeu entier : la requête calcule alors
sur la page reçue et le dit, en console et au volet Diagnostic.
Un site qui a du cache (Varnish, CDN, cache de son CMS) veut que les données d'une dataviz
passent par son domaine, sous une URL qui identifie la donnée. Avec
relay-url, la source n'appelle plus le portail : elle appelle le site, sous la forme
<relais>/<hôte>/<chemin>?<requête>, et c'est le relais du site
qui va chercher la réponse et la met en cache. La source reste en mode adaptateur : filtres,
regroupements, tri et pagination délégués vivent dans l'URL, que le relais transmet telle quelle.
| Attribut | Type | Défaut | Description |
|---|---|---|---|
relay-url | String | "" | Préfixe du relais cachable du site hôte (ADR-155, #1232) : le chemin que le site a choisi pour sa route de relais (relay-url="/relais"), ou une URL absolue (relay-url="https://site.example/relais"). Vide : la valeur de window.DSFR_DATA_RELAY, sinon aucun relais — et alors rien ne change. Avec un relais, toute requête GET vers une autre origine part sous la forme relais/hôte/chemin?requête (le préfixe du relais, l'hôte de la cible, puis son chemin et sa requête), en mode adaptateur comme en mode URL : filtres, regroupements, tri et pagination délégués restent dans l'URL, que le relais transmet telle quelle. Deux cibles donnent deux URL, une même requête donne la même URL au caractère près : le site peut les mettre en cache. Le relais est une route que LE SITE HÔTE fournit, selon le contrat docs/RELAY.md (relais de référence : proxy/relay/node/). Aucune instance publique n'en expose. Sur une requête relayée, aucun en-tête n'est envoyé : ni headers, ni api-key-ref (la clé appartient au relais, qui l'ajoute par hôte), ni cookie. Si l'un de ces attributs est posé, la source l'écrit une fois en console. Ne passent PAS par le relais, et gardent le chemin habituel (direct ou proxy-url) : une URL relative ou de même origine ; une requête POST (mode SQL de Grist, method="POST") ; une cible hors https, sur un port explicite ou avec identifiants ; un chemin que le relais refuserait (%2f, %2e, //…) — ces deux derniers cas avec un avertissement. fetch-mode="export" sur l'API Tabular (export Parquet) retombe sur la pagination, relayée. Une URL de relais de plus de 8 000 caractères part quand même au relais, qui répond 414. Si le relais répond 503 (place momentanément prise : il n'a pas de file d'attente), la requête est réessayée trois fois au plus, après le délai qu'il annonce ; jamais sur un 429. Sans rapport avec cache-ttl : la durée de cache se règle sur le relais. |
<!-- La requête part vers
/donnees-relais/data.economie.gouv.fr/api/explore/v2.1/catalog/datasets/rappelconso/records?… -->
<dsfr-data-source id="rappels" api-type="opendatasoft" relay-url="/donnees-relais"
base-url="https://data.economie.gouv.fr" dataset-id="rappelconso">
</dsfr-data-source>
<!-- Ou une fois pour toute la page, avant le chargement des composants -->
<script>window.DSFR_DATA_RELAY = '/donnees-relais';</script>
Le relais est une route que le site hôte fournit : la bibliothèque n'en
embarque aucun, et aucune instance publique n'en expose. Son contrat (liste blanche d'hôtes,
lecture seule, en-têtes de cache, statuts d'erreur) est décrit dans docs/RELAY.md
du dépôt, avec un relais Node de référence (proxy/relay/node/) et une suite de
conformance à lancer contre son propre relais.
Deux cibles, deux URL ; une même requête, la même URL au caractère près : le chemin et la requête sont repris tels que l'adaptateur les a écrits, sans réencodage ni tri des paramètres. Deux visiteurs qui posent les mêmes filtres dans un ordre différent produisent deux URL, donc deux entrées de cache.
Aucun en-tête ne part au relais : ni headers, ni
api-key-ref, ni cookie. Si le portail exige une clé, c'est le relais qui l'ajoute,
depuis sa configuration — la clé quitte le navigateur. La source l'écrit une fois en console
quand l'un de ces attributs est posé. Hors relais, ils restent actifs.
Ce qui ne passe pas par le relais, et garde le chemin habituel (direct, ou
proxy-url) : une URL relative ou de même origine ; une requête POST
(method="POST", mode SQL de Grist) ; une cible hors https, sur un port
explicite ou avec identifiants, et un chemin que le relais refuserait (%2f,
%2e, //…) — ces deux derniers cas avec un avertissement en console.
fetch-mode="export" sur l'API Tabular (export Parquet) retombe sur la pagination,
qui est relayée. Une URL de relais de plus de 8 000 caractères part quand même au relais, qui
répond 414 : la bibliothèque ne le contourne jamais en silence.
Sans rapport avec proxy-url ni cache-ttl :
proxy-url désigne le proxy CORS (endpoints dédiés et /cors-proxy, cible
dans un en-tête, donc non cachable) ; cache-ttl est un repli hors ligne côté
navigateur. La durée de cache d'une donnée relayée se règle sur le relais.
Erreurs : les statuts du relais sont lus par le
barème d'erreurs (hôte hors liste : 403 ; URL trop longue : 414 ; amont en
panne : 502 ou 504 ; débit dépassé : 429), et « Détails techniques » montre l'URL du relais
réellement appelée. Un relais sans file d'attente répond 503 quand un visiteur charge d'un coup
plus de dataviz qu'il n'a de places : la requête est alors réessayée trois fois au plus, après
le délai annoncé par Retry-After ; jamais sur un 429.
<dsfr-data-source id="prix-ct" url="https://data.economie.gouv.fr/api/explore/v2.1/catalog/datasets/prix-controle-technique/records" transform="results" ></dsfr-data-source>
<dsfr-data-source id="api-privee" url="https://mon-api.gouv.fr/data" method="POST" headers='{"Authorization": "Bearer TOKEN"}' params='{"limit": 100, "filters": {"annee": 2024}}' transform="data.items" refresh="60" ></dsfr-data-source>
Avec paginate, chaque changement de page dans un dsfr-data-list
en aval declenche un nouvel appel API avec les paramètres de pagination.
L'ordre des lignes est celui de l'API : la source n'en connaît ni les paramètres de tri ni
un identifiant de ligne, et ne lit qu'une page à la fois. Une API qui pagine sans ordre total
peut rendre une ligne sur deux pages et n'en jamais servir une autre, sans que rien le
signale. C'est à l'URL de porter le tri de l'API sur une colonne unique — dans
url, ou dans params.
<dsfr-data-source id="browse" api-type="opendatasoft" dataset-id="rappelconso-v2-gtin-trie" base-url="https://data.economie.gouv.fr" server-side paginate page-size="20" ></dsfr-data-source> <dsfr-data-list source="browse" columns="modeles_ou_references:Produit, categorie_produit:Catégorie, marque_produit:Marque" pagination="20"> </dsfr-data-list>
Auto-extraction : en mode paginate, pas besoin de transform="data".
Les données sont automatiquement extraites depuis json.data et les metadonnees de pagination
(json.meta) sont stockees pour calculer le nombre total de pages.
L'attribut use-proxy force le passage par le proxy CORS generique.
Utile quand l'API cible ne renvoie pas les headers CORS necessaires.
<dsfr-data-source id="ghibli-films" url="https://ghibliapi.vercel.app/films" use-proxy ></dsfr-data-source>
Fonctionnement : en dev, la requête est routee via le middleware Vite
/cors-proxy. En production, elle passe par l'endpoint equivalent sur le serveur proxy
(<votre-proxy>/cors-proxy).
L'URL cible est transmise dans le header X-Target-URL.
L'adapter gère automatiquement la construction d'URL, la pagination et le parsing. Pagination automatique (toutes les pages en une seule requête) jusqu'a 1 000 records.
<dsfr-data-source id="elus" api-type="opendatasoft" base-url="https://data.iledefrance.fr" dataset-id="elus-regionaux" where="sexe = 'F'" ></dsfr-data-source> <dsfr-data-query id="stats" source="elus" group-by="groupe_politique" aggregate="nom:count:nb" order-by="nb:desc"> </dsfr-data-query> <dsfr-data-chart source="stats" type="bar" label-field="groupe_politique" value-field="nb"> </dsfr-data-chart>
L'adapter ODS gère la pagination, le tri serveur, la recherche full-text et les facettes. Ici un simple datalist pagine.
<dsfr-data-source id="src-ods" api-type="opendatasoft" dataset-id="rappelconso-v2-gtin-trie" base-url="https://data.economie.gouv.fr" server-side page-size="20" ></dsfr-data-source> <dsfr-data-list source="src-ods" columns="modeles_ou_references:Produit, categorie_produit:Catégorie, marque_produit:Marque, date_publication:Date" server-sort pagination="20"> </dsfr-data-list>
L'adapter Grist se connecte aux instances Grist (grist.numerique.gouv.fr ou getgrist.com). Il détecté automatiquement si la requête necessite le mode SQL (groupement, agrégation, opérateurs avancés) ou le mode Records standard.
<dsfr-data-source id="grist-data" api-type="grist" base-url="https://grist.numerique.gouv.fr/api/docs/DOC_ID/tables/TABLE_ID/records" headers='{"Authorization": "Bearer TOKEN"}' ></dsfr-data-source> <dsfr-data-chart source="grist-data" type="bar" label-field="nom" value-field="valeur"> </dsfr-data-chart>
Aplatissement automatique : l'adapter Grist aplatit lui-même la structure
records[].fields de la réponse — aucun dsfr-data-normalize n'est
nécessaire en mode adapter. Le flatten de normalize ne sert que si vous appelez
l'API Grist en mode URL brute (attribut url sans api-type),
où la réponse arrive telle quelle.
Avec server-side, la source charge une page a la fois.
Le dsfr-data-list en aval pilote la navigation entre les pages.
<dsfr-data-source id="srv" api-type="opendatasoft" dataset-id="rappelconso-v2-gtin-trie" base-url="https://data.economie.gouv.fr" server-side page-size="20" ></dsfr-data-source> <dsfr-data-list source="srv" columns="modeles_ou_references:Produit, categorie_produit:Catégorie, marque_produit:Marque" server-sort pagination="20"> </dsfr-data-list>
L'attribut data permet de passer des données JSON directement,
sans aucun appel HTTP. Utile pour les exemples, prototypes ou données statiques.
<dsfr-data-source id="inline" data='[{"nom":"Paris","pop":2161000},{"nom":"Lyon","pop":516092},{"nom":"Marseille","pop":870731}]' ></dsfr-data-source> <dsfr-data-chart source="inline" type="bar" label-field="nom" value-field="pop"> </dsfr-data-chart>
api-key-ref)
Au lieu d'ecrire les tokens directement dans les attributs HTML, declarez-les une seule fois
dans un registre global window.DSFR_DATA_KEYS, puis referencez-les par nom
avec api-key-ref. La clé est injectée comme header Authorization.
<script> window.DSFR_DATA_KEYS = { tmdb: 'Bearer eyJhbGciOiJIUzI1NiJ9...', monapi: 'Token abc123' }; </script> <dsfr-data-source id="films" url="https://api.themoviedb.org/3/movie/popular" api-key-ref="tmdb" transform="results" ></dsfr-data-source>
Comportement : si api-key-ref est défini et que la clé existe
dans window.DSFR_DATA_KEYS, elle est injectée en header Authorization
à chaque requête. Si la clé n'est pas trouvee, un avertissement est emis dans la console et
la requête est envoyee sans authentification. Fonctionne avec les deux modes (URL brute et adapter).
Securite : les clés restent lisibles par tout script sur la page
(même modele de securite que l'attribut headers). Cette fonctionnalite est une
commodite pour separer la declaration des clés du markup, pas un mecanisme de protection.
Pour les clés sensibles, utilisez un proxy serveur.
L'attribut refresh re-execute la requête a intervalles reguliers (en secondes).
Fonctionne dans les deux modes (URL brute et adapter).
<dsfr-data-source id="live" api-type="opendatasoft" base-url="https://data.opendatasoft.com" dataset-id="mon-dataset" refresh="30" ></dsfr-data-source>
Quand le chargement échoue, les blocs branchés sur la source ne montrent plus
Erreur de chargement: HTTP 503: Service Unavailable en rouge. Chacun garde sa place et
affiche, dans un encart neutre, une phrase que l'usager peut comprendre ; le code HTTP, l'adresse
appelée et l'heure sont repliés dans « Détails techniques ». La phrase dépend de la cause :
| Cause | Ce que lit l'usager | Action proposée |
|---|---|---|
| Service indisponible (HTTP 5xx, délai dépassé) ou réponse bloquée (pas d'en-tête CORS) | Données momentanément indisponibles. Le service qui publie ces chiffres ne répond pas pour l'instant. | Réessayer |
| Hors connexion | Vous semblez hors connexion. Les chiffres s'afficheront quand la connexion reviendra. | Réessayer, et nouvel essai automatique au retour du réseau |
| Service très sollicité (HTTP 429) | Le service est très sollicité. Réessayez dans quelques instants. | Réessayer — jamais de nouvel essai automatique |
| Données introuvables (HTTP 404) | Ces données ne sont plus publiées à cette adresse. Le producteur les a peut-être déplacées ou retirées. | Lien « Consulter la page de ces données », si source-page est posé ; sinon aucune |
| Accès restreint (HTTP 401, 403) | Ces données ne sont pas accessibles publiquement. | Aucune |
| Page mal réglée (HTTP 400, source mal configurée) | Cet affichage n'a pas pu être construit. Le problème vient de la page, pas de votre connexion. | Aucune |
« Réessayer » recharge la source à l'identique (mêmes filtres, même page) : c'est la méthode
reload(), déclenchée par la commande { reload: true }. Après un échec
hors connexion, la source se relance d'elle-même, une fois, au retour du réseau. L'erreur de
configuration d'un composant d'affichage (attribut invalide) n'est pas concernée :
elle reste une alerte, écrite pour l'intégrateur.
Pour dire la panne avec vos mots, posez error-message sur la source :
| Attribut | Type | Défaut | Description |
|---|---|---|---|
error-message | String | "" | Phrase affichée à l'usager quand cette source est en panne (#1203), à la place du message du barème — dans les blocs branchés sur la source comme dans le bandeau dsfr-data-source-status. error-message="Les chiffres de la DGFiP sont en cours de mise à jour." : à poser quand l'intégrateur sait mieux que la bibliothèque dire qui publie les données et quoi faire. Ne remplace QUE la phrase usager d'un échec de chargement : le code HTTP, l'adresse appelée et l'heure restent dans « Détails techniques », l'Error de dsfr-data-error et la console ne changent pas. Sans effet sur une erreur de configuration de la source. |
source-page | String | "" | Adresse de la page PUBLIQUE de ces données (#1222) : la page du jeu sur le portail du producteur, celle qu'un usager peut lire — pas l'adresse d'API. source-page="https://data.economie.gouv.fr/explore/dataset/prix-carburants/" : quand la source répond « introuvable » (404, 410), le bloc en erreur et le bandeau dsfr-data-source-status proposent le lien « Consulter la page de ces données ». Sans cet attribut, aucun lien : la bibliothèque ne déduit l'adresse ni de base-url, ni de dataset-id, ni de resource, et ne montre jamais l'adresse d'API ni celle du relais (relay-url). Sans effet sur les autres pannes (service indisponible, hors connexion, accès restreint…) et sur le chargement : l'attribut ne déclenche aucune requête. N'est admise qu'une adresse http(s) ou relative ; toute autre (javascript:, data:…) est ignorée, avec un avertissement en console. |
Quand la source répond « introuvable » (HTTP 404 ou 410), l'usager n'a rien à réessayer : le
producteur a déplacé ou retiré le jeu. Posez source-page sur la source avec l'adresse
de la page publique de ces données — la page du jeu sur le portail du producteur,
celle qu'un usager peut lire, pas l'adresse d'API. Les blocs en erreur et le bandeau proposent
alors le lien « Consulter la page de ces données ».
source-page, aucun lien. La bibliothèque ne déduit l'adresse ni de
base-url, ni de dataset-id, ni de resource : sur un portail
Opendatasoft, la page /explore/dataset/<id>/ disparaît avec le jeu, donc le lien
déduit mènerait à une page introuvable ; sur l'API Tabular, l'identifiant de ressource ne donne
pas le jeu sans une requête de plus. L'adresse d'API n'est jamais proposée à l'usager, ni celle
du relais (relay-url).http(s) ou relative. Toute autre
(javascript:, data:…) est ignorée, avec un avertissement en console.<dsfr-data-source id="prix" api-type="opendatasoft" base-url="https://data.economie.gouv.fr" dataset-id="prix-carburants" source-page="https://data.economie.gouv.fr/explore/dataset/prix-carburants/"> </dsfr-data-source>
L'encart complet est fait pour un bloc de graphique ; dans une tuile de KPI il doublerait sa
hauteur. Un <dsfr-data-kpi> dont la source est en panne garde donc sa place et sa
hauteur : « — » à la place du chiffre, son libellé (l'usager doit savoir quel chiffre
manque) et une phrase courte. Il n'affiche jamais un nombre : ni 0, ni le chiffre du chargement
précédent. Les autres afficheurs (graphique, jauge comprise, liste, podium, display) gardent
l'encart complet.
| Cause | Phrase de la tuile | Sans bandeau, la tuile porte aussi |
|---|---|---|
| Service indisponible, réponse bloquée | Chiffre momentanément indisponible | Réessayer · Détails |
| Hors connexion | Vous semblez hors connexion | Réessayer · Détails |
| Service très sollicité (HTTP 429) | Le service est très sollicité | Réessayer · Détails |
| Données introuvables (HTTP 404, 410) | Ce chiffre n'est plus publié à cette adresse | Lien de source-page · Détails |
| Accès restreint (HTTP 401, 403) | Ce chiffre n'est pas accessible publiquement | Détails |
| Page mal réglée (HTTP 400, source mal configurée) | Ce chiffre n'a pas pu être affiché | Détails |
error-message remplace la phrase, comme dans l'encart complet. « Détails » est replié, à
la suite de la phrase : il donne le code HTTP, l'adresse appelée et l'heure. Avec un bandeau
dsfr-data-source-status, la tuile ne garde que le tiret, le libellé et la phrase : le
bouton, le lien et le détail sont dans le bandeau. Dans une tuile étroite, le lien ou « Détails »
passent à la ligne, et la tuile gagne une ligne. Une valeur absente des données
(null, champ manquant) n'est pas une panne : la tuile affiche « — » et son libellé, sans phrase.
Accessibilité : le tiret est masqué aux lecteurs d'écran (aria-hidden), qui lisent
le libellé et la phrase ; la figure se nomme « libellé : phrase », sans chiffre. Le bouton
« Réessayer » garde 44 px de haut.
Pendant une panne, aucun bloc ne garde à l'écran une ligne du dernier chargement : sans date, elle serait prise pour le chiffre du jour. Le graphique, le KPI, la liste, le podium et les fiches rendent le message décrit plus haut. Les blocs ci-dessous, qui n'avaient pas de rendu de panne, suivent la même règle.
| Bloc | Pendant la panne | Phrase (service indisponible) |
|---|---|---|
dsfr-data-a11y | Le tableau est retiré. « Télécharger en CSV » est désactivé (aria-disabled, il garde le focus) et ne produit aucun fichier. La description lue avec le graphique ne compte plus de lignes. | Données momentanément indisponibles. Le tableau et le téléchargement reviendront avec les données. |
dsfr-data-map, dsfr-data-map-layer | La couche en panne est vidée : formes, grappes, carte de chaleur, entrée de légende, bandeau de troncature, et ses clones dans les encarts. Le fond reste manipulable. Si plus rien n'est tracé, le message des autres blocs est centré sur la carte ; si une autre couche trace encore, une ligne en bas de carte nomme la couche (son label, sinon le libellé de sa légende). La description lue ne compte plus ses marqueurs. | Données momentanément indisponibles (message centré) Couche « Bornes de recharge » : données momentanément indisponibles. (ligne d'état) |
dsfr-data-list, dsfr-data-display en pagination serveur | Un changement de page refusé garde la page précédente et le dit ; la pagination et l'adresse (url-sync) reviennent sur la page affichée, et redemander la page refusée relance la source. Tout autre échec (rafraîchissement, tri, filtre) rend le message de panne, sans aucune ligne. | La page 4 n'a pas pu être chargée. Vous consultez toujours la page 3. |
dsfr-data-facets, dsfr-data-search | Les filtres sont désactivés (aria-disabled, toujours atteignables au clavier) ; leurs compteurs et le compte de résultats ne sont plus affichés. La sélection et le terme déjà posés sont gardés et s'appliquent de nouveau au retour des données. « Réinitialiser les filtres » et la touche Échap restent actifs. | Filtres momentanément indisponibles. |
Export de dsfr-data-list (export) | Aucun fichier tant que la liste est en panne. | — |
dsfr-data-repeat | Les lignes estampées depuis le dernier chargement sont retirées — texte du gabarit et blocs qu'il contient —, avec les sources scopées (scopes) qu'elles servaient. Elles sont recréées au chargement réussi. | Données momentanément indisponibles (message à la place des lignes) |
Les phrases suivent la cause, comme l'encart : hors connexion, « Vous semblez hors connexion » ;
HTTP 429, « Le service est très sollicité » ; données introuvables, « Ces données ne sont plus
publiées à cette adresse » ; accès restreint, « Ces données ne sont pas accessibles
publiquement ». Pour les filtres et la description du tableau équivalent, la cause suit la
mention « indisponibles » : « Filtres indisponibles. Vous semblez hors connexion. »
error-message s'applique partout. Quand le refus vient de la page elle-même
(HTTP 400), les filtres restent actifs : le filtre posé peut être la cause du refus, et le
retirer est alors le seul remède.
Avec un bandeau dsfr-data-source-status, aucun de ces blocs ne porte « Réessayer » ni
n'annonce la panne une seconde fois : la carte et le tableau équivalent gardent leur phrase, sans
bouton. Sans bandeau, la carte porte « Réessayer » (dans le message centré comme dans la ligne
d'état) ; le tableau équivalent et les filtres n'annoncent rien de plus, la panne étant déjà dite
par le graphique, la carte ou la liste qu'ils accompagnent.
Pendant un nouvel essai (« Réessayer », retour du réseau, rafraîchissement périodique) : le graphique, le KPI, la liste et les fiches affichent leur indicateur de chargement ; le tableau équivalent, la carte et les filtres gardent leur rendu de panne, phrase comprise, jusqu'au résultat — rien ne clignote. Au chargement réussi, tout revient sans rechargement de la page.
stale-if-error
Par défaut, une source en panne ne laisse aucun de ses anciens chiffres à l'écran. Pour un jeu
qui change peu — un recensement, un budget voté, un référentiel —, un trou fait croire la
page cassée alors que le chiffre d'il y a dix minutes est encore le chiffre du jour. Posez
stale-if-error sur la source, avec une durée en secondes : pendant une panne
passagère, les blocs branchés dessus gardent leurs derniers chiffres,
atténués, sous l'étiquette « Non actualisé » et l'heure de leur relevé.
| Attribut | Type | Défaut | Description |
|---|---|---|---|
stale-if-error | Number | 0 | Garder à l'écran le DERNIER RELEVÉ pendant une panne passagère (#1288) : durée, en secondes, pendant laquelle les derniers chiffres reçus restent affichables. Absent ou 0 (défaut) : une source en panne ne laisse aucun ancien chiffre à l'écran, chaque bloc dit la panne. stale-if-error="3600" : si la source échoue après avoir livré, les blocs branchés dessus (KPI, graphique, liste, fiches, podium, carte, tableau équivalent) gardent leurs chiffres, atténués, sous l'étiquette « Non actualisé » et la phrase « Chiffres relevés aujourd’hui à 14 h 32. » — l'heure est celle où la page a reçu ces chiffres. L'export CSV reste possible, daté dans son libellé et dans le nom du fichier. Passé la durée (comptée depuis le relevé), les blocs reviennent d'eux-mêmes au message de panne. C'est à l'intégrateur de le décider, jeu par jeu : un chiffre d'il y a une heure a un sens pour un recensement, pas pour un stock ou des places libres. La valeur EST la borne : il n'existe pas de relevé sans durée. Le relevé n'est jamais affiché, quelle que soit la valeur, quand : la cause n'est pas passagère (données introuvables 404 / 410, accès restreint 401 / 403, page mal réglée) ; l'appel en échec n'est pas celui qui a produit les chiffres (filtre, page, tri ou emprise changés) ; rien n'a encore été reçu. Les filtres (dsfr-data-facets, dsfr-data-search) restent désactivés pendant la panne. Mémoire : celle de la page. Une page OUVERTE pendant la panne n'a rien à montrer. getData() et getDataCache rendent toujours vide pendant la panne : seuls les afficheurs de la bibliothèque lisent le relevé, par getLastKnownData(). |
<!-- Pendant une panne passagère, les derniers chiffres restent une heure, datés --> <dsfr-data-source id="aides" api-type="opendatasoft" base-url="https://data.economie.gouv.fr" dataset-id="aides-industrie" stale-if-error="3600"> </dsfr-data-source> <dsfr-data-kpi source="aides" value="beneficiaires:sum" label="Bénéficiaires"></dsfr-data-kpi> <dsfr-data-chart source="aides" type="bar" label-field="region" value-field="beneficiaires"></dsfr-data-chart>
C'est à vous de le décider, jeu par jeu : la bibliothèque ne sait pas si un chiffre est un recensement ou un nombre de places libres. Ne le posez pas sur une donnée que l'usager utilise pour agir tout de suite (prix, stock, file d'attente, alerte) : un tiret y est moins dangereux qu'une valeur d'il y a une heure. La valeur est la borne — il n'existe pas de relevé sans durée —, comptée depuis le relevé et non depuis la panne.
| Bloc | Pendant la panne, avec stale-if-error | Texte |
|---|---|---|
dsfr-data-kpi | Le chiffre reste, en gris de texte ; le filet devient pointillé ; ni couleur de seuil, ni tendance, ni lignes complémentaires. La tuile garde sa hauteur. Son nom accessible porte la date avec le chiffre. | Relevé à 14 h 32 · non actualisé |
dsfr-data-chart (cadre officiel compris) | Le tracé reste, désaturé, sous une mention. Le nom accessible du graphique porte la date. databox-date n'est pas touché : c'est la date de la donnée, pas celle du relevé. | « Non actualisé » — Chiffres relevés aujourd'hui à 14 h 32. |
dsfr-data-list, dsfr-data-display, dsfr-data-podium, dsfr-data-repeat | Les lignes restent, sous la même mention. Recherche, filtres de colonne, tri et pagination locaux répondent. En pagination ou en tri serveur, la page affichée reste et les autres sont neutralisées (aria-disabled) : une autre page serait une autre requête. | Chiffres relevés aujourd'hui à 14 h 32. Les autres pages reviendront avec les données. |
dsfr-data-a11y | Le tableau reste ; sa légende devient visible et porte l'étiquette et la date. Le téléchargement reste possible : le libellé du bouton et le nom du fichier portent la date du relevé, le contenu reste brut. | Données du graphique — « Non actualisé » chiffres relevés aujourd'hui à 14 h 32 Télécharger en CSV (chiffres relevés à 14 h 32) données_relevé-2026-10-13-14h32.csv |
Export de dsfr-data-list | Permis lui aussi, daté de la même façon. | Exporter CSV (chiffres relevés à 14 h 32)export_relevé-2026-10-13-14h32.csv |
dsfr-data-map, dsfr-data-map-layer | Les formes restent tracées. Toute la carte est atténuée dès qu'une couche est périmée — formes grises, contours en pointillé, légende comprise — et une ligne en bas de carte la date ; avec plusieurs couches, elle nomme celle qui n'est pas à jour. Une bulle ouverte sur un point porte la même note. | Points relevés aujourd'hui à 14 h 32. Couche « Bornes de recharge » : points relevés aujourd'hui à 14 h 32. |
dsfr-data-facets, dsfr-data-search | Inchangé : désactivés pendant la panne, comme sans l'attribut. | Filtres momentanément indisponibles. |
dsfr-data-source-status | Le bandeau ne promet plus que les blocs « s'afficheront » : il dit qu'ils n'ont pas été actualisés. Il ne dit pas l'heure — chaque bloc dit la sienne, deux sources pouvant ne pas dater du même instant. | Une partie des chiffres de cette page n'a pas pu être actualisée |
Un autre jour que celui du relevé, la date est écrite en entier — « Chiffres relevés le
12 octobre à 18 h 05. » —, jamais « hier » ni « il y a 3 min ». Hors connexion, la mention
ajoute « Vous semblez hors connexion. » ; error-message s'y ajoute aussi.
Le relevé n'est jamais affiché, quelle que soit la durée, et le bloc rend le message de panne décrit plus haut, quand :
stale-if-error, ou sa valeur n'est pas une durée strictement positive (elle est alors ignorée, avec un avertissement en console) ;bbox a changé — les lignes gardées répondent à une autre question. Il en va de même quand un attribut d'une requête branchée sur la source change pendant la panne ;
Pendant un nouvel essai, les chiffres relevés restent en place — pas de retour à
l'indicateur de chargement, une source en refresh ne fait pas clignoter la page — et
le bouton dit « Nouvel essai en cours… ». Au chargement réussi, l'étiquette disparaît et les
nouveaux chiffres s'affichent. Sans bandeau, chaque mention porte « Réessayer » et
« Détails techniques » ; avec un bandeau, il les porte seul.
Pour un script de la page, rien ne change : getData() et
getDataCache(id) rendent toujours vide pendant la panne. Le relevé n'est lu que par
les composants d'affichage.
Sans bandeau, huit blocs branchés sur une source en panne affichent huit messages et huit boutons
« Réessayer ». Posez <dsfr-data-source-status> en haut du contenu : la panne est
dite une fois par source, avec le seul bouton « Réessayer ». Les blocs concernés
gardent leur place et leur message court, sans bouton ; les blocs d'une autre source ne sont pas
touchés. Le bandeau n'affiche rien tant qu'aucune source n'est en panne.
| Attribut | Type | Défaut | Description |
|---|---|---|---|
source | String | "" | Id de la source à suivre. Vide : toutes les sources de la page, un message par source en panne. Une étape intermédiaire (dsfr-data-query…) branchée sur la source est suivie avec elle : c'est la source qui charge qui est relancée. |
<!-- La panne de "prix" est dite ici, une fois --> <dsfr-data-source-status source="prix"></dsfr-data-source-status> <dsfr-data-source id="prix" api-type="opendatasoft" base-url="https://data.economie.gouv.fr" dataset-id="prix-carburants" error-message="Les prix sont en cours de mise à jour. Revenez dans quelques minutes."> </dsfr-data-source> <dsfr-data-kpi source="prix" valeur="avg:prix" label="Prix moyen"></dsfr-data-kpi> <dsfr-data-chart source="prix" type="bar" label-field="ville" value-field="prix"></dsfr-data-chart>
Accessibilité : le bandeau est une région role="status" (annonce polie, qui
n'interrompt pas la lecture). Les blocs en erreur ne sont plus des alertes assertives : sans
bandeau, chacun est une région role="status" ; avec bandeau, seul le bandeau annonce.
Le bouton « Réessayer » est un bouton DSFR d'au moins 44 px de haut.
Une source qui échoue après avoir déjà livré ne jette pas ses lignes, mais
elles ne passent plus pour à jour. Les composants n'en affichent aucune : ils gardent le
message de panne décrit plus haut — tableau équivalent, carte et export compris —, sauf quand
la source le demande par stale-if-error, et alors
datées. Pour un script de la page, la règle est la même, avec ou sans cet attribut :
| Lecture | Source à jour | Source en panne |
|---|---|---|
element.getData() | les lignes | null ([] sur une requête, une jointure, un empilement, un pivot) |
DsfrData.getDataCache(id) | les lignes | undefined |
element.isStale() | false | true, jusqu'au prochain chargement réussi — nouvel essai en cours compris |
element.getLastKnownData()DsfrData.getLastKnownData(id) | { data, loadedAt } | { data, loadedAt, stale: { since, cause, originId, staleIfError?, sameRequest? } } |
loadedAt est la date (Date.now()) du dernier chargement réussi,
since celle du premier échec depuis, cause la cause du barème
(service-indisponible, hors-connexion, service-sollicite,
donnees-introuvables, acces-restreint, page-mal-reglee,
reponse-bloquee) et originId la source qui a réellement échoué.
Quand la source porte stale-if-error, staleIfError en rend la durée
(en secondes) et sameRequest dit si l'appel en échec est celui qui avait produit
ces lignes.
Les lignes et leur date sont rendues ensemble : on ne peut pas obtenir les unes sans l'autre.
Le marquage se lève au premier chargement réussi ; une source remise en attente d'un filtre
(require-where) ou retirée de la page n'en garde aucune.
<script type="module">
document.addEventListener('dsfr-data-error', (e) => {
if (e.detail.sourceId !== 'prix' || e.detail.lastLoadedAt === undefined) return;
const { data, loadedAt, stale } = document.getElementById('prix').getLastKnownData();
// À n'afficher que DATÉ : « Données du … · mise à jour impossible »
console.log(data.length, 'lignes du', new Date(loadedAt).toLocaleString('fr-FR'), stale.cause);
});
</script>
Les transformateurs suivent la même règle : une requête, un dsfr-data-normalize,
une jointure, un empilement ou un pivot dont la source est en panne relaient l'erreur et ne
recalculent rien sur les anciennes lignes — ni quand un de leurs attributs change, ni quand
une autre de leurs entrées se recharge.
Le composant émet des événements personnalises via le système data-bridge :
dsfr-data-loaded : Données chargées avec succès (detail : { sourceId, data, loadedAt } — loadedAt date le chargement)dsfr-data-loading : Chargement en coursdsfr-data-error : Erreur de chargement (detail : { sourceId, error, attemptedUrl?, lastLoadedAt? }, le message de l'Error porte le code HTTP). lastLoadedAt n'est présent que si la source avait déjà livré : ses lignes restent en mémoire, périmées. Le message lisible des blocs ne change pas cet événement : avec la trace console, il reste la voie de l'intégrateur.cache-fallback : Les données proviennent du cache serveur suite a une erreur API (mode DB uniquement)| Méthode | Retour | Description |
|---|---|---|
reload() | void | Force le rechargement des données, à l'identique. C'est ce que déclenche « Réessayer ». |
getData() | unknown | Retourne les données actuelles. null avant le premier chargement, en attente d'un filtre, et pendant une panne : les lignes d'avant la panne ne sont pas rendues comme à jour (voir les dernières lignes, périmées et datées). |
getLastKnownData() | { data, loadedAt, stale? } | undefined | Les lignes du dernier chargement réussi et leur date. stale ({ since, cause, originId }) est présent pendant une panne, absent sinon. undefined si la source n'a jamais rien livré. |
isStale() | Boolean | true quand la source garde des lignes périmées : de sa panne jusqu'au prochain chargement réussi. |
isLoading() | Boolean | Indique si un chargement est en cours |
getError() | Error | null | Retourne l'erreur eventuelle |
getAdapter() | ApiAdapter | null | Retourne l'adapter actif (mode adapter uniquement, null en mode URL brute) |
getEffectiveWhere(excludeKey?) | String | Retourne la clause WHERE fusionnee (statique + tous les overlays dynamiques).
Le paramètre excludeKey permet d'exclure un overlay spécifique (utilise par les facettes). |
Choisir le bon mode : Utilisez le mode URL brute pour vous connecter a n'importe quelle API REST. Utilisez le mode adapter pour les APIs connues (ODS, Tabular, Grist) afin de beneficier de la pagination automatique, des filtres server-side et de la construction d'URL geree.
server-side vs pagination automatique : Sans server-side, l'adapter
charge toutes les données (multi-pages) en une seule operation. Avec server-side,
il charge une seule page et attend les commandes des composants en aval. Utilisez
server-side pour les grands datasets avec un dsfr-data-list pagine.
Pipeline de données : dsfr-data-source s'integre dans un pipeline :
dsfr-data-source → dsfr-data-normalize →
dsfr-data-query / dsfr-data-search / dsfr-data-facets →
composants de visualisation.
Securite : les headers, tokens et clés dans window.DSFR_DATA_KEYS
sont visibles dans le code source HTML. Ne les utilisez que pour des clés a acces restreint (lecture seule)
ou dans des contextes proteges (intranet, applications internes).
Pour une separation propre entre markup et clés, utilisez api-key-ref.
Sur une instance en mode serveur, une table Grist privée peut être publiée
depuis l'app Sources : la source devient url="https://<instance>/api/public/sources/<jeton>/records"
transform="records", la clé reste sur le serveur, et le lien se révoque à tout moment
(quiconque l'a lit la table).