Composant visuel intermediaire qui affiche un champ de recherche DSFR et filtre les données en amont avant de les redistribuer aux composants en aval.

Visuel et intermediaire : Ce composant affiche un champ de recherche ET redistribue les données filtrees aux composants en aval via le système d'événements. Il se combine naturellement avec dsfr-data-facets.

Position dans le pipeline

dsfr-data-source → dsfr-data-normalize → dsfr-data-search → dsfr-data-facets → dsfr-data-display / dsfr-data-list

La recherche reduit le jeu de données, les facettes affinent ensuite. Les compteurs de facettes se recalculent dynamiquement.

Cas d'usage

Attributs

AttributTypeDéfautDescription
idString-Requis. Identifiant de la sortie (données filtrees).
sourceString""ID de la source de données a ecouter
fieldsString""Champs sur lesquels rechercher (virgule-séparés). Vide = tous les champs
placeholderStringRechercher\u2026Placeholder du champ de saisie
labelStringRechercherLabel du champ (accessible)
debounceNumber300Délai en ms avant déclenchement du filtre après la dernière frappe
min-lengthNumber0Nombre minimum de caractères avant déclenchement
highlightBooleanfalseAjoute un champ _highlight à chaque record avec les termes trouvés marqués en <mark>
operatorSearchOperatorcontainsMode de recherche quand elle est calculée dans le navigateur, casse et accents ignorés : contains (le terme est une sous-chaîne), starts (un mot commence par le terme), words (tous les mots du terme sont présents). Une recherche confiée à un serveur (server-search, ou cible de context dont l'API a de quoi chercher) suit la comparaison de ce serveur.
sr-labelBooleanfalseSi true, le label est en sr-only (visuellement masque, accessible)
countBooleanfalseAffiche un compteur de résultats sous le champ (compte serveur meta.total en server-search), séparateur de milliers français (#728). Ce compteur reste visible dès qu'une donnée a circulé ; seule sa nature de région live dépend de la chaîne aval (#654). Tant que l'amont attend un filtre (require-where), il cède la place au message d'attente : annoncer « 0 résultats » avant toute requête laisserait croire à une page vide.
count-labelString""Nom compté par le compteur de count, à la place de « résultat » : count-label="établissement" affiche « 12 345 établissements ». Une forme seule prend un « s » au pluriel ; pour un pluriel irrégulier, donner les deux formes séparées par une barre verticale : count-label="cheval|chevaux", count-label="prix|prix".
url-search-paramString""Nom du paramètre d'URL à lire comme terme de recherche initial. Vide = désactivé
url-syncBooleanfalseSynchronise l'URL quand l'utilisateur tape (replaceState)
server-searchBooleanfalseActive le mode recherche serveur. Au lieu de filtrer localement, envoie une commande { where } au source upstream (dsfr-data-query server-side) qui re-fetche les données avec le filtre search.
search-templateString""Template pour la recherche serveur. {q} est remplacé par le terme de recherche, {fields} par les champs de fields séparés par | (#1026) — la grammaire colon des champs multiples, un OU entre eux. Si vide et server-search activé, lu depuis l'adaptateur de la source amont (gabarit par défaut de chaque adaptateur déclarant serverSearch : recherche plein texte native, ou {fields}:contains:{q} traduit en OU entre colonnes — la casse et les accents suivent l'API ; table des capacités d'ARCHITECTURE). Ex. personnalisés : '{q} IN nom', 'nom|commune:contains:{q}'. Une clause que l'adaptateur ne sait pas transmettre (caractère que son API ne sait pas porter), ou une source sans adaptateur, retombe sur une recherche locale, signalée en console.
contextString""Id du dsfr-data-context auquel s'enregistrer (#678, ADR-104) : la recherche est confiée au contexte, qui la diffuse à ses cibles et porte l'URL (url-sync et url-search-param sont ignorés — le paramètre est nommé d'après le champ, ou via url-param-map du contexte). fields doit nommer UN seul champ (le OU entre champs, a|b:contains:q #1026, reste propre à server-search). Le contexte peut être déclaré après la recherche dans la page. Vide = comportement autonome. La recherche n'est pas un filtre contains (ADR-159) : CHAQUE CIBLE LA TRADUIT, selon ce que son API sait faire. - Opendatasoft : le moteur de recherche du portail, borné au champ (search(champ, "terme")) — par DÉBUT DE MOT, casse et accents ignorés : « aliment » trouve « alimentation », « mentation » non. Le champ doit être du texte. - Tabular, Grist : une sous-chaîne (champ:contains:terme), casse ignorée, accents respectés. - INSEE Melodi, adaptateur tiers : la source calcule la recherche dans le navigateur, sur les lignes chargées (dans la limite de max-records), avec operator, casse et accents ignorés. Confiée à l'API, la recherche entre dans le where de la source : une cible en server-side et des facettes server-facets voisines comptent le même ensemble. Ce composant laisse alors passer les lignes de sa source sans les refiltrer, et son compteur (count) annonce le total du serveur. Calculée dans le navigateur, elle exige que la source charge le jeu : une cible en server-side passe en erreur de configuration.
idle-messageStringIDLE_MESSAGE_DEFAULTMessage rendu quand l'amont attend un filtre (require-where, #690). Distinct de « aucune donnée » : aucune requête n'a été faite. Vide, le libellé par défaut est utilisé.

Recherche serveur sur plusieurs colonnes (#1026)

En server-search, le gabarit search-template accepte {fields} : il devient les champs de fields séparés par |, la grammaire des champs multiples (nom|commune:contains:martin, un OU entre les champs). C'est le gabarit par défaut sur Tabular, {fields}:contains:{q}, traduit en or=(nom__contains.martin,commune__contains.martin) : le compteur lit le total serveur, juste sur tout le jeu et non sur les lignes chargées. fields est alors obligatoire. Côté serveur, contains est insensible à la casse mais sensible aux accents ; la recherche locale, elle, les ignore — on ne l'aligne pas, pour ne pas rendre deux compteurs différents selon le chemin. Un terme que l'API ne sait pas transmettre (, . ( ) " & sur Tabular) retombe sur une recherche dans le navigateur, sur les seules lignes chargées, et un avertissement le dit.

Recherche portée par le contexte (ADR-159)

Avec context="id", la recherche est confiée au dsfr-data-context, qui la diffuse à ses sources cibles, porte l'URL et le tag « Recherche : terme ». Ce n'est pas un filtre contains : un filtre est une règle exacte écrite par l'intégrateur, une recherche est ce que tape un visiteur. Le contexte porte le terme, le champ unique de fields et operator, et chaque source cible traduit la recherche selon ce que son API sait faire.

Qui cherche, selon la source cible
Source cibleQui chercheComparaison
Opendatasoft le moteur du portail, borné au champ : search(champ, "terme") par début de mot, casse et accents ignorés : « aliment » trouve « alimentation », « mentation » non. Le champ doit être du texte.
Tabular, Grist l'API : champ:contains:terme sous-chaîne, casse ignorée, accents respectés
INSEE Melodi, adaptateur tiers la source, dans le navigateur sur les lignes chargées (sous max-records), avec operator, casse et accents ignorés
source en mode URL personne : la commande est refusée, et signalée en console brancher la recherche sur cette source, sans context

Confiée à l'API, la recherche entre dans le where de la source : une cible en server-side pagine les seules lignes trouvées, et des dsfr-data-facets server-facets voisines comptent le même ensemble. Le composant laisse alors passer les lignes de sa source sans les refiltrer, et son compteur (count) annonce le total du serveur. Calculée dans le navigateur, elle exige que la source charge le jeu : une cible en server-side, ou des server-facets voisines, passent en erreur de configuration. Le résultat n'est donc pas identique d'un fournisseur à l'autre ; c'est ce tableau qui le dit.

Source en panne

Quand la source échoue, le champ est désactivé (lecture seule, aria-disabled, toujours atteignable au clavier) sous la phrase « Filtres momentanément indisponibles. », et le compte de résultats n'est plus affiché. Le terme déjà saisi est gardé : il s'applique de nouveau au retour des données. La touche Échap efface toujours le terme. Voir Quand la source est en panne.

En attente d'un filtre

Quand l'amont porte require-where, aucune requête n'est partie : le compteur de count céderait la place à un « 0 résultat » trompeur. Il est remplacé par le texte de idle-message (« Choisissez un filtre pour afficher les données » par défaut), qui dit que rien n'a encore été demandé. Le champ de saisie, lui, reste utilisable — c'est souvent lui qui pose le premier filtre et débloque la source. Le mécanisme est décrit sur dsfr-data-source.

Modes de recherche :
contains (défaut) : sous-chaine insensible a la casse et aux accents.
starts : chaque mot du champ doit commencer par le terme.
words : tous les mots saisis doivent être présents (dans n'importe quel champ, n'importe quel ordre). Le plus utile pour la recherche multi-criteres.

Exemple 1 : recherche simple + tableau

Recherche textuelle sur les champs Produit, Marque et Catégorie. Le tableau se filtre en temps réel avec un compteur de resultats.

Code

<dsfr-data-search id="searched" source="clean"
  fields="Produit, Marque, Catégorie"
  placeholder="Produit, marque ou catégorie..."
  operator="words"
  min-length="2"
  count>
</dsfr-data-search>

<dsfr-data-list source="searched"
  columns="Produit, Catégorie, Marque, Date"
  sort="Date:desc"
  pagination="10">
</dsfr-data-list>

Exemple 2 : recherche + facettes + cartes

La recherche et les facettes se combinent. La recherche reduit le jeu de données, les facettes affinent. Les cartes affichent les resultats.

Code

<dsfr-data-search id="searched" source="clean"
  fields="Produit, Marque, Catégorie"
  placeholder="Rechercher un produit rappele..."
  operator="words"
  count>
</dsfr-data-search>

<dsfr-data-facets id="filtered" source="searched"
  fields="Catégorie, Marque"
  labels="Catégorie:Catégorie | Marque:Marque"
  max-values="6">
</dsfr-data-facets>

<dsfr-data-display source="filtered" cols="3" pagination="9">
  <template>
    <div class="fr-card">
      <div class="fr-card__body">
        <div class="fr-card__content">
          <h3 class="fr-card__title">{{Produit}}</h3>
          <p class="fr-card__desc">{{Marque}}</p>
        </div>
        <div class="fr-card__footer">
          <p class="fr-badge fr-badge--sm">{{Catégorie}}</p>
        </div>
      </div>
    </div>
  </template>
</dsfr-data-display>

Exemple 3 : recherche + KPI + graphique

Les KPI et le graphique se recalculent en temps réel selon la recherche. Rechercher par nom d'entreprise, département ou region.

Code

<dsfr-data-search id="searched" source="clean"
  fields="Entreprise, Region, Departement"
  placeholder="Entreprise, region, departement..."
  operator="words"
  count>
</dsfr-data-search>

<dsfr-data-kpi source="searched" value="count" label="Projets" color-token="bleu"></dsfr-data-kpi>
<dsfr-data-kpi source="searched" value="montant_investissement:sum" label="Investissement" format="euro"></dsfr-data-kpi>

<dsfr-data-query id="stats" source="searched"
  group-by="Region"
  aggregate="nombre_beneficiaires:sum:Beneficiaires"
  order-by="Beneficiaires:desc"
  limit="10">
</dsfr-data-query>

<dsfr-data-chart source="stats" type="bar"
  label-field="Region"
  value-field="Beneficiaires"
  selected-palette="categorical">
</dsfr-data-chart>

Événements

En plus de la ré-émission des données filtrées sur le data-bridge sous son propre id (dsfr-data-loaded / -loading / -error), le composant émet un événement custom à chaque recherche effective :

ÉvénementdetailDescription
dsfr-data-search-change { sourceId, term, count } Émis sur document (bubbles, composed) après application d'un terme : sourceId = id du composant, term = terme recherché, count = nombre de résultats.
document.addEventListener('dsfr-data-search-change', (e) => {
  const { sourceId, term, count } = e.detail;
  console.log(`${count} résultats pour « ${term} » (${sourceId})`);
});