ac_datagrid_pro.js

Composant JavaScript pur — zéro dépendance — backend agnostique

Version 3.14.0 Licence Perpétuelle Compatible Laravel · Symfony · Node · PHP

1. Quick start

Une seule balise <script>, quelques lignes de configuration — et votre grille est en ligne. Pas de npm, pas de bundler, pas de framework.

<script src="ac_datagrid_pro.js"></script>

Choisissez votre mode selon votre situation :

✦ Mode data — zéro backend

Passez un tableau JS directement. Aucun serveur, aucune API. Idéal pour prototyper ou afficher des données fixes.

⇄ Mode url — avec API

Pointez vers un endpoint REST. La grille gère pagination, tri et filtres côté serveur automatiquement.

Mode data — zéro backend

Le moyen le plus rapide de démarrer. Vous n'avez besoin d'aucun serveur, d'aucune API, d'aucune base de données. Passez votre tableau et la grille fait le reste — tri, filtres, pagination, tout est géré côté client.

<div id="ma-grille"></div>
<script src="ac_datagrid_pro.js"></script>
<script>
new AcDatagridPro({
    target: '#ma-grille',
    data: [
        { id: 1, nom: 'Dupont',  prenom: 'Marie',  ville: 'Bordeaux' },
        { id: 2, nom: 'Martin',  prenom: 'Thomas', ville: 'Lyon'     },
        { id: 3, nom: 'Petit',   prenom: 'Claire', ville: 'Paris'    },
    ],
    colonnes: [
        { champ: 'id',     titre: 'ID',     editable: false },
        { champ: 'nom',    titre: 'Nom'    },
        { champ: 'prenom', titre: 'Prénom' },
        { champ: 'ville',  titre: 'Ville'  },
    ]
});
</script>
Zéro appel réseau Dès que cfg.data est un tableau, cfg.url devient facultatif — la grille tourne entièrement en mémoire (tri, filtres, pagination, CRUD simulé), sans jamais toucher fetch().

Sans preset déclaré, le preset mini s'applique : affichage simple, lecture seule, pagination. Ajoutez preset: 'recherche' pour obtenir tri, recherche et filtres automatiques sur vos données.

Mode url — avec API

Pointez vers votre endpoint REST. La grille envoie des requêtes HTTP (GET par défaut pour la lecture, voir contrat d'API) et attend une réponse normalisée.

serverside n'est jamais automatique Contrairement à ce qu'une version antérieure de cette documentation affirmait, cfg.serverside vaut toujours false par défaut, que vous passiez data ou url. Avec url + serverside:false, la grille fonctionne quand même : un seul chargement récupère toutes les lignes, puis tri/filtres/pagination se font côté client — correct sur un petit jeu de données, mais à éviter au-delà de quelques milliers de lignes. Pour un vrai gros volume, précisez serverside: true explicitement.
new AcDatagridPro({
    target:     '#ma-grille',
    url:        '/api/clients',   // votre endpoint REST
    preset:     'edition',        // CRUD activé
    serverside: true,             // recommandé dès que le volume dépasse quelques milliers de lignes
    colonnes: [
        { champ: 'id',    titre: 'ID',    editable: false },
        { champ: 'nom',   titre: 'Nom',   requis: true    },
        { champ: 'email', titre: 'Email', type: 'email'   },
    ]
});

Le contrat d'API attendu est décrit dans la section API référence.

2. Cas concrets

1

Tableau filtrable — zéro backend

Vous avez des données JSON (un export, une réponse d'API tierce, un fichier statique) et vous voulez les afficher avec tri, recherche et filtres. Aucun serveur nécessaire.

const produits = [
    { id: 1, ref: 'REF-001', nom: 'Câble HDMI 2m',    categorie: 'Câbles',  prix: 12.90, stock: 48 },
    { id: 2, ref: 'REF-002', nom: 'Souris sans fil',   categorie: 'Périph.', prix: 29.90, stock: 12 },
    { id: 3, ref: 'REF-003', nom: 'Clavier mécanique', categorie: 'Périph.', prix: 89.00, stock: 5  },
    { id: 4, ref: 'REF-004', nom: 'Hub USB-C 7 ports', categorie: 'Câbles',  prix: 45.00, stock: 0  },
];

new AcDatagridPro({
    target:  '#grille-produits',
    data:    produits,
    preset:  'recherche',   // tri + recherche globale + filtres automatiques
    colonnes: [
        { champ: 'ref',       titre: 'Réf.',      editable: false, largeur: '100px' },
        { champ: 'nom',       titre: 'Produit'                                     },
        { champ: 'categorie', titre: 'Catégorie', type: 'select',
          options: [{ id: 'Câbles', libelle: 'Câbles' }, { id: 'Périph.', libelle: 'Périphériques' }] },
        { champ: 'prix',      titre: 'Prix',      type: 'number', monetaire: true   },
        { champ: 'stock',     titre: 'Stock',     type: 'number',
          format_conditionnel: [{ operateur: '=', valeur: 0, couleur: '#dc2626', gras: true }] },
    ]
});

Le preset recherche gère tri, recherche et filtres entièrement en mémoire, sans aucune requête serveur. La colonne stock passe en rouge gras quand elle vaut 0.

2

Grille éditable — CRUD avec API

Votre backend expose un endpoint REST. La grille gère l'interface complète : ajout, modification, suppression avec confirmation — sans une ligne de JS supplémentaire.

new AcDatagridPro({
    target:     '#grille-contacts',
    url:        '/api/contacts',
    preset:     'edition',
    serverside: true,
    colonnes: [
        { champ: 'id',        titre: 'ID',       editable: false },
        { champ: 'nom',       titre: 'Nom',      requis: true  },
        { champ: 'prenom',    titre: 'Prénom',   requis: true  },
        { champ: 'email',     titre: 'Email',    type: 'email'   },
        { champ: 'telephone', titre: 'Tél.',     type: 'tel'     },
        { champ: 'id_statut', titre: 'Statut',   type: 'select',
          options: [
              { id: 1, libelle: 'Actif'   },
              { id: 2, libelle: 'Inactif' },
              { id: 3, libelle: 'Prospect'},
          ]
        },
    ]
});

Le preset edition inclut : tri, recherche, filtres, CRUD en modale (création / modification / suppression), et persistance de l'état en localStorage.

Backend REST — 5 routes, pas un endpoint unique Contrairement à une version antérieure de ce document, il n'y a plus d'endpoint unique piloté par un champ action. La grille appelle des routes distinctes selon l'action (GET pour lire, POST/PUT pour écrire, DELETE pour supprimer) — voir la section API référence pour le détail exact, et cfg.routes pour adapter la grille à un backend qui suit d'autres conventions (endpoint unique compris, si c'est votre cas).
3

Grille complète — export, épingles, totaux

Pour les cas métier avancés : le preset complet active toutes les fonctionnalités.

new AcDatagridPro({
    target:          '#grille-commandes',
    url:             '/api/commandes',
    preset:          'complet',
    serverside:      true,
    export:          true,
    export_formats:  ['csv', 'xlsx'],
    export_filename: 'commandes',
    theme:           'techno',
    colonnes: [
        { champ: 'id',          titre: 'N°',    editable: false, largeur: '70px'  },
        { champ: 'client',      titre: 'Client', requis: true                 },
        { champ: 'date',        titre: 'Date',   type: 'date'                   },
        { champ: 'montant_ht',  titre: 'HT',     type: 'number', monetaire: true,
          agregat: 'somme',
          format_conditionnel: [{ operateur: '>=', valeur: 10000, couleur: '#16a34a', gras: true }] },
        { champ: 'montant_ttc', titre: 'TTC',    type: 'number', monetaire: true, agregat: 'somme' },
        { champ: 'statut',      titre: 'Statut', type: 'select',
          options: [
              { id: 'brouillon', libelle: 'Brouillon' },
              { id: 'envoyee',   libelle: 'Envoyée'   },
              { id: 'payee',     libelle: 'Payée'     },
              { id: 'annulee',   libelle: 'Annulée'   },
          ]
        },
    ]
});

Le preset complet inclut tout : CRUD modale + cellule (double-clic), ajout inline, épingles ★, duplication de ligne, export CSV/XLSX, import CSV, totaux et formatage conditionnel. Les totaux ne sont plus déclarés dans une liste globale totals : chaque colonne porte son propre agregat (somme par défaut ; voir Colonnes).

3. Options avancées

Presets

Les presets sont des configurations pré-packagées. Choisissez le plus proche de votre besoin et surchargez uniquement ce qui diffère.

Chaîne de fusion : Défauts → Preset → Config du développeur. La config explicite l'emporte toujours.

mini DÉFAUT

Lecture seule, clientside, pagination uniquement. Sans tri, sans recherche, sans filtres.

recherche

mini + recherche globale, filtres automatiques, persistance en localStorage. Lecture seule.

edition

recherche + CRUD en modale (création/modification/suppression), serverside activé.

edition_avancee

edition + édition cellule par cellule (double-clic), ajout inline, lignes épinglées.

complet

Comportement standard de la classe, aucune restriction — totaux, duplication de lignes, export CSV/XLSX, import CSV. Tableau vide dans le code source : garantit qu'il ne diverge jamais des défauts de la classe.

mini et recherche — adaptés au mode data Ces deux presets conviennent bien aux données statiques (zéro backend). Avec url et un gros volume, préférez edition ou supérieur, et pensez à ajouter serverside: true explicitement — voir la mise en garde de la section précédente.

Options générales

OptionTypeDéfautDescription
targetstringrequis*Sélecteur CSS du conteneur (#ma-grille). *Ou un élément DOM directement ; facultatif si passé comme 1er argument du constructeur.
dataarraynullDonnées statiques directes. url devient facultatif si présent.
urlstringURL de l'endpoint API. data devient facultatif si présent.
colonnesarrayrequisDéfinition des colonnes (voir section Colonnes)
presetstring'mini'mini · recherche · edition · edition_avancee · complet
serversideboolfalseJamais automatique, quel que soit data ou url — voir la mise en garde du Quick Start
paginationbooltrueActiver la pagination
longueur_defautint25Lignes par page
edit_modestring'inline'inline · modal · cell
ajout_modestring'inline'inline · modal
themestring'sobre'sobre · techno · nature · pastel
ajoutbooltrueAfficher le bouton d'ajout
deleteboolfalseActiver la suppression
read_onlyboolfalseRaccourci pour désactiver tout le CRUD
recherchebooltrueBarre de recherche globale
filtre_autobooltrueLigne de filtres automatiques par colonne
cookiesbooltruePersistance état (tri, filtres, colonnes masquées, page) en localStorage
epinglebooltrueColonne étoile — épingler des lignes en tête
epingle_page_1boolfalseN'envoie les identifiants épinglés au serveur qu'en page 1 (optimisation réseau)
duplicationboolfalseBouton de duplication de ligne (⧉)
exportboolfalseActive le bouton export — formats dans export_formats
export_formatsarray['xlsx']['xlsx'], ['csv'] ou les deux (menu déroulant si 2 formats)
export_filenamestring'export'Nom du fichier exporté, sans extension
import_csvboolfalseImport CSV en 3 étapes avec mapping visuel
totauxbooltrueLigne de totaux en pied de tableau (agrégat par colonne, voir Colonnes)
filtresarray[]Filtres externes (selects au-dessus de la grille) — voir Filtres externes
defaut_actifboolfalsefalse = comportement d'origine inchangé, la clé defaut d'un filtre externe reste posée visuellement (selected) mais ignorée dans les requêtes au premier chargement. true = le defaut s'applique réellement dès le premier chargement, persiste en localStorage, et resynchronise visuellement le select. v3.13.0
modal_titrestringnullTitre de la modale d'édition
sous_grillesarray[]Tableau de définitions de sous-grilles (pas un objet unique) — voir Sous-grilles
routesobject{}Surcharge la construction/interprétation d'une route REST, action par action — voir API référence
enTetesfunctionnull(action, params) => ({...}) — en-têtes HTTP additionnels (authentification)
labelsobject{}Surcharger les libellés i18n
debugboolfalseLogs détaillés en console
Options disparues crud (objet {insert,update,delete}), sort, resize et formatConditionnel (interrupteur global) n'existent plus. Le tri et le redimensionnement des colonnes sont désormais toujours actifs, sans interrupteur ; le CRUD granulaire se pilote via ajout / delete / edit_mode ; le formatage conditionnel s'active simplement en le déclarant sur une colonne, sans clé globale.

Colonnes

Chaque colonne est un objet dans le tableau colonnes :

PropriétéTypeDéfautDescription
champstringrequisNom du champ dans les données JSON
titrestring= champTitre affiché dans l'en-tête
typestring'text'text · select · date · datetime · number · email · url · tel · color · textarea · checkbox
editablebool|stringtruefalse = colonne en lecture seule (ex: clé primaire, par heuristique). 'creation' = saisissable uniquement à la création, verrouillé et visible en modification — voir la note ci-dessous 'creation' v3.13.0
pkboolfalsetrue = désigne explicitement la clé primaire, prioritaire sur l'heuristique editable:false pour getPK() (détection de ligne existante vs nouvelle, route PUT vs POST). Exclue d'office du corps envoyé au backend REST, même si elle n'est pas editable:false — même protection que côté ac_datagrid.php v3.13.0
formulestringnullColonne calculée, ex. '({prix_ht} * {tva}) * {nbr_articles}'{champ} référence toute autre colonne de la même ligne. Force editable:false automatiquement (même normalisation que ac_datagrid.php). Recalculée en direct côté navigateur à la saisie des champs sources — voir la note ci-dessous v3.14.0
visiblebooltruefalse = champ transmis aux requêtes mais non affiché
requisboolfalseValidation côté grille avant envoi
largeurstringautoLargeur CSS ('60px', '15%')
optionsarrayOptions statiques [{id, libelle}] pour type select
monetaireboolfalseAffichage monétaire avec séparateurs et symbole
agregatstring'somme'somme · moyenne · min · max · nombre — fonction du total en pied de tableau
totalbooltrueInclure cette colonne dans la ligne de totaux
format_conditionnelarray/objectFormatage conditionnel (voir ci-dessous)
defautmixed''Valeur par défaut à la création d'une nouvelle ligne v3.13.0
placeholderstringnullTexte indicatif dans le champ vide (attribut placeholder natif), tous types de saisie sauf select et checkbox v3.13.0
aidestringnullBulle d'aide (attribut title), sur le champ en édition ET sur l'en-tête de colonne v3.13.0
minnumbernullBorne minimale native (attribut min), colonnes type:'number' v3.13.0
maxnumbernullBorne maximale native (attribut max), colonnes type:'number' v3.13.0
stepnumbernullPas d'incrément natif (attribut step), colonnes type:'number' v3.13.0
maxlengthintnullLongueur maximale (attribut maxlength natif), colonnes type:'text' et type:'textarea' v3.13.0
decimalesintnullnull = comportement d'origine inchangé. Posé : plafond de décimales à l'affichage sans zéro forcé (5 reste 5, 5,25 reste 5,25 avec decimales:2). S'applique à toute colonne type:'number', avec ou sans monetaire:true v3.13.0
editable: 'creation' — v3.13.0. Un champ saisissable seulement à la création, verrouillé ensuite : sur une ligne nouvelle, traité comme editable:true (inline, modale, duplication). Sur une ligne existante : non éditable en inline ; en modale, le champ reste visible mais verrouillé — un input disabled affichant la valeur (libellé résolu pour un select), sans attribut data-champ, donc jamais soumis à l'API. requis sur un tel champ n'est validé qu'à la création.
formule — v3.14.0. Colonne calculée à partir d'autres colonnes de la même ligne, syntaxe type tableur : '({prix_ht} * {tva}) * {nbr_articles}'. Opérateurs supportés : + - * / ( ).
  • Non éditable directementeditable:false est forcé automatiquement dès qu'une colonne porte formule, même normalisation que côté ac_datagrid.php.
  • Recalcul en direct — un champ verrouillé (input disabled, mais data-champ conservé contrairement à editable:'creation') affiche la valeur, mise à jour à chaque frappe dans un champ source. Fonctionne en inline, en modale, et immédiatement à l'ouverture d'une ligne dupliquée.
  • Champ source manquant ou non numérique → la colonne calculée reste vide, jamais 0 par défaut.
  • Backend externe et calcul serveur — contrairement à ac_datagrid.php (qui recalcule systématiquement côté PHP, sans jamais faire confiance au client), ac_datagrid_pro n'a pas de code serveur propre : c'est la valeur calculée côté navigateur qui est envoyée telle quelle dans le corps de la requête sauvegarder (POST/PUT), au même titre que les autres champs. Un développeur qui souhaite une vérification serveur (recommandé si la donnée a une valeur métier sensible, ex. facturation) doit l'implémenter dans son propre backend REST — la classe ne le fait pas à sa place.
  • Sécurité côté navigateur — après substitution des {champ} par leur valeur numérique, l'expression est validée par une whitelist stricte (chiffres, points, opérateurs arithmétiques uniquement) avant toute évaluation : aucune exécution de code arbitraire possible, même si formule provenait d'une source non fiable.
  • Pas de chaînage dans cette version — une colonne formule ne peut pas référencer une autre colonne formule. Prévu pour la 4.0.0.

Formatage conditionnel

{ champ: 'salaire', titre: 'Salaire', type: 'number',
  format_conditionnel: [
    { operateur: '<', valeur: 30000, couleur: '#dc2626'               },  // rouge
    { operateur: '>', valeur: 50000, couleur: '#16a34a', gras: true  },  // vert gras
  ]
}

Opérateurs : = == < > <= >= — Propriétés de règle : couleur · gras (bool, optionnel). Les règles sont évaluées dans l'ordre, la première qui correspond gagne.

Forme alternative pour un seuil unique (deux/trois couleurs autour d'une valeur de référence) : { seuil, superieur, egal, inferieur } — voir la documentation ac_datagrid pour le détail, le mécanisme est strictement identique des deux côtés.

background et icon ne sont plus supportés Une version antérieure (non finalisée, marquée TODO dans son propre code) esquissait des propriétés background et icon par règle, avec seulement 3 opérateurs (=, >, <). La version actuelle couvre 6 opérateurs mais seulement couleur/gras. Ces deux propriétés pourraient revenir dans une prochaine version si le besoin se confirme.

Filtres externes v3.13.0

Des selects (ou champs date) de filtrage peuvent être placés au-dessus de la grille, en plus du filtre_auto par colonne. Contrairement à ac_datagrid (PHP), il n'y a pas de sql_options — chaque filtre fournit directement son tableau d'options en JS, le transport REST n'ayant pas de notion de requête SQL à exécuter côté classe.

new AcDatagridPro('#ma-grille', {
    url: '/api/commandes',
    filtres: [
        {
            id:        'filtre_statut',
            label:     'Statut',
            champ_sql: 'statut',            // clé transmise au backend
            options:   [
                { id: 0, libelle: 'En cours' },
                { id: 1, libelle: 'Terminée' },
            ],
            defaut: 0,                       // v3.13.0 — nécessite defaut_actif ci-dessous
        },
    ],
    defaut_actif: true,   // v3.13.0 — sans ça, 'defaut' reste cosmétique
    colonnes: [ /* ... */ ],
});
defaut_actif — v3.13.0. La clé defaut d'un filtre pose bien la bonne option comme selected à la construction du select (voir facade.js), mais seule defaut_actif:true (au niveau grille) fait réellement passer cette valeur dans les requêtes dès le premier chargement, et la fait persister en localStorage d'un rechargement de page à l'autre. Sans defaut_actif:true, le select affiche la bonne valeur mais la grille charge sans filtre au premier chargement — un décalage purement visuel, resté silencieux jusqu'à cette version.
PropriétéTypeDescription
idstringIdentifiant du filtre, utilisé en interne (clé de filtresActifs)
labelstringLibellé affiché au-dessus du champ
champ_sqlstringNom du paramètre transmis au backend dans la requête
typestringselect (défaut) · date_debut · date_fin
optionsarray[{id, libelle}] — ignoré pour les types date
defautmixedValeur présélectionnée. Nécessite defaut_actif:true au niveau grille pour avoir un effet réel sur les requêtes v3.13.0

Thèmes

4 thèmes CSS embarqués dans la classe — aucun fichier externe, activables via theme :

ValeurDescription
sobreBleu navy, sobre et professionnel — idéal pour les back-offices
technoMode sombre, accents cyan — pour les interfaces techniques
natureTons verts et terreux — pour les applications grand public
pastelTons doux et rosés — pour les interfaces créatives et RH

CRUD — Modes d'édition

edit_mode: 'inline' (défaut)

La ligne entière se transforme en formulaire. Boutons ✔ Valider et ✕ Annuler à droite.

edit_mode: 'modal'

Un formulaire s'ouvre dans une modale. Tous les champs sur une seule vue. Idéal pour les formulaires complexes.

edit_mode: 'cell'

Double-clic sur une cellule pour l'éditer directement. Entrée = valider, Échap = annuler. Idéal pour les corrections rapides.

Granularité CRUD

// Désactiver uniquement la suppression
ajout: true, delete: false

// Lecture seule (raccourci)
read_only: true
Comportement après INSERT en mode serveur Après validation d'un ajout, la grille recharge depuis le backend. La nouvelle ligne apparaît à sa place selon le tri actuel. Écoutez acgr:apres-insert pour une navigation personnalisée.

Widgets externes v1.1.0

Une colonne peut remplacer son champ de saisie par défaut par un composant JavaScript riche que vous avez écrit vous-même — un sélecteur de carte, un champ téléphone international, un éditeur enrichi... Le composant n'a besoin d'aucune dépendance particulière ; il respecte simplement un contrat minimal, comme une prise électrique.

Le contrat côté widget

Votre classe JavaScript doit exposer :

Déclaration dans la colonne

{
  champ: 'adresse',
  titre: 'Adresse',
  widget: 'MonWidgetCarte',       // nom de la classe, exposée sur window
  widget_ancre: 'div',            // 'div' (widget visuel) ou 'input' (widget qui gère une valeur simple)
  widget_options: { zoom: 14 }    // transmis tel quel au constructeur, complété automatiquement
}
Fallback automatique Si la classe indiquée par widget n'existe pas sur window, ou si son instanciation échoue, la cellule retombe automatiquement sur un champ texte classique — la grille ne casse jamais, même si le widget n'a pas été chargé sur la page.
Widgets qui font leur propre appel réseau (ex. ac_big_select) widget_options reçoit automatiquement _acgr_url (= cfg.url), _acgr_id et _champ — utile pour un widget qui a besoin de savoir sur quelle grille/quel champ il travaille. Attention cependant : certains widgets de la Classothèque (comme ac_big_select) font leur propre appel réseau, avec leur propre format (FormData, action options_recherche) — indépendamment de cfg.routes. Contre un backend REST externe, ce widget précis n'enverra pas une requête compatible avec votre API tant qu'il n'aura pas été adapté ou que votre backend n'implémente pas cette route spécifique séparément. Sujet en cours de normalisation.

Lignes épinglées ★

Activez la colonne étoile avec epingle: true (activé par défaut). L'utilisateur épingle des lignes en cliquant sur l'étoile — elles remontent en tête et restent visibles sur toutes les pages.

✦ Comportement exclusif ac_datagrid_pro / ac_datagrid Les lignes épinglées restent visibles en tête sur toutes les pages, pas seulement sur la page courante. Le backend reçoit les IDs épinglés à chaque requête et retourne leurs données complètes dans epinglees (sauf si epingle_page_1: true, qui limite l'envoi à la page 1 par optimisation réseau).
new AcDatagridPro({
    target:  '#grille',
    url:     '/api/clients',
    preset:  'edition_avancee',  // épingles activées par edition_avancee et complet
    colonnes: [ ... ]
});

Export CSV / XLSX

Activez l'export avec export: true et choisissez le(s) format(s) avec export_formats.

new AcDatagridPro({
    url:             '/api/data',
    export:          true,
    export_formats:  ['csv', 'xlsx'],
    export_filename: 'mes_clients',
    colonnes: [ ... ]
});
Zéro dépendance Les deux formats sont générés entièrement en JavaScript vanilla. Le CSV utilise le séparateur point-virgule et l'encodage UTF-8 BOM pour compatibilité Excel. Le XLSX est du XML zippé au format Office Open XML natif, compatible Excel 2010+ et LibreOffice.

Sous-grilles (maître/détail)

API entièrement revue Une version antérieure exigeait de créer deux instances séparées et de les relier manuellement via acgr:row-click + setFiltreParent(). Ce n'est plus nécessaire : les sous-grilles se déclarent directement dans la config du parent, sous sous_grilles (un tableau, pas un objet unique) — la grille les enregistre et les initialise elle-même à l'ouverture d'une ligne.
new AcDatagridPro({
    target:  '#praticiens',
    url:     '/api/praticiens',
    colonnes: [ ... ],

    sous_grilles: [{
        subgrid_param: 'id_praticien',   // champ transmis au backend de la sous-grille
        url:           '/api/patients',
        colonnes: [
            { champ: 'id',   titre: 'ID',  editable: false },
            { champ: 'nom',  titre: 'Nom' },
            { champ: 'tel',  titre: 'Tél.' },
        ],
    }],
});

Chaque objet de sous_grilles accepte les mêmes options qu'une grille normale (routes, theme, serverside, preset...). Plusieurs sous-grilles sont possibles (tableau, pas de limite à une seule).

Événements DOM

La grille émet des événements DOM personnalisés, qui remontent (bubbles) sur l'élément cible :

document.querySelector('#ma-grille')
  .addEventListener('acgr:apres-insert', function(e) {
      console.log('Champs insérés :', e.detail.donnees);
  });
Formes corrigées ci-dessous Une version antérieure de cette section documentait des formes de detail et un événement (acgr:avant-suppression) qui n'existaient pas dans le code de l'époque — la confirmation de suppression passe toujours par AcNotifModale.confirmer() (ou confirm() natif en repli), appelé en interne, jamais par un événement DOM annulable. Cela reste vrai : aucun événement n'intercepte la boîte de confirmation elle-même. Les formes ci-dessous sont vérifiées contre le code source.
acgr:before_delete — v3.13.0 Ceci dit, un événement annulable existe désormais, mais ailleurs dans le flux : acgr:before_delete (voir plus bas) se déclenche après que l'utilisateur a confirmé la suppression dans la boîte de dialogue, mais avant que la requête réseau ne parte. Il permet un blocage programmatique par règle métier (ex. ligne déjà facturée) — un cas d'usage différent de la confirmation UI ci-dessus, pas une résurrection de l'ancien acgr:avant-suppression jamais implémenté.
acgr:apres-chargement v3.12.0

Émis après chaque chargement réussi (initial, pagination, tri, recherche, filtre)

{ total, page, grille_id }
acgr:row-click

Clic sur une ligne (hors boutons/inputs)

{ id, grille_id, selected, row }row (objet complet de la ligne) ajouté en v3.12.0
acgr:apres-insert

Ligne insérée avec succès

{ donnees, grille_id }donnees = champs envoyés au serveur (pas de row/id distincts)
acgr:apres-update

Ligne mise à jour avec succès

{ donnees, grille_id }
acgr:apres-suppression

Ligne supprimée avec succès

{ id, grille_id }
acgr:apres-suppression-multiple

Suppression groupée des lignes épinglées terminée (bouton ⛔ de la barre d'outils)

{ ids, nb_ok, erreurs, grille_id }ids = lignes visées, erreurs = tableau { id, message } pour celles en échec
acgr:charger-sous-grille

Événement interne — déclenché à l'ouverture d'une ligne pour initialiser sa sous-grille. À usage interne : la déclaration via sous_grilles (voir section dédiée) s'en charge, pas besoin de l'écouter directement.

{ masterId, container, parentId }
acgr:log

Émis sur document (pas sur le conteneur de la grille) après chaque CREATE/UPDATE/DELETE réussi — pratique pour brancher sa propre traçabilité côté page hôte

{ action, id, table, grille, donnees }action = 'CREATE'|'UPDATE'|'DELETE'. donnees ajouté en v3.13.0 : champs de la ligne concernée.
acgr:before_delete v3.13.0

Émis sur le conteneur de la grille, avant tout appel réseau, sur suppression simple ET groupée. Annulable via event.preventDefault() — bloque la suppression sans aller-retour serveur. Distinct de la confirmation AcNotifModale.confirmer() (voir l'encart ci-dessus) : ce nouvel événement se déclenche après la confirmation utilisateur mais avant la requête réseau, pour un blocage programmatique (règle métier), pas une confirmation UI.

{ id, ligne, table, grille_id }ligne = objet complet de la ligne visée (ou null si introuvable localement)
acgr:erreur

Erreur retournée par le backend ou réseau

{ message, code }

Accessibilité v1.1.0

La grille pose automatiquement les attributs ARIA nécessaires pour qu'un lecteur d'écran ou une navigation 100% clavier comprenne sa structure et ses états — aucune configuration requise.

Les libellés sont personnalisables via cfg.labels, comme tous les autres textes de la grille.

Méthodes publiques

Liste corrigée Une version antérieure documentait load(), setData() et getSelection() — seule la première existe, sous un autre nom (charger()). setData() et getSelection() n'ont jamais été construites dans la reconstruction actuelle. À garder en réserve pour une prochaine version.
instance.charger()

Recharge les données depuis le backend (ou re-filtre en mode data).

instance.setFiltreParent(champ, valeur)

Définit un filtre parent et recharge. Utilisé en interne pour les sous-grilles ; disponible aussi en usage manuel si besoin d'un contrôle plus fin que la déclaration via sous_grilles.

instance.destroy()

Détruit l'instance, annule une requête réseau en vol s'il y en a une, vide le conteneur et libère les listeners.

AcDatagridPro.getVersion()

Méthode statique. Retourne la version du composant ("3.14.0").

Scrollbar horizontale miroir v1.0.5

Lorsque la grille déborde horizontalement, une scrollbar miroir apparaît au-dessus du tableau, synchronisée bidirectionnellement avec la scrollbar native du bas. Elle permet de faire défiler la grille sans avoir à atteindre le bas de la page.

Comportement automatique

Cas des onglets cachés

Quand la grille est dans un onglet display:none au chargement, son clientWidth est 0 et le calcul reste à zéro. Utiliser les hooks publics pour déclencher un recalcul une fois l'onglet visible.

Hooks publics JS v1.0.5

Chaque instance expose trois mécanismes pour forcer le recalcul de la scrollbar miroir depuis l'extérieur. Tous sont opt-in — les grilles existantes fonctionnent sans modification.

1. Méthode directe sur le conteneur

document.getElementById('mon_id').acgrRefreshScroll();

2. Événement DOM ciblé

const container = document.getElementById('mon_id');
container.dispatchEvent(new CustomEvent('acgr:refresh-scroll'));

3. Événement global — toutes les grilles

Dispatché sur document — toutes les grilles de la page recalculent simultanément. Idéal pour les systèmes d'onglets : un seul appel suffit.

// Dans le gestionnaire d'activation d'onglet :
document.getElementById('tab-' + id).classList.add('active');

// requestAnimationFrame garantit que display:block est effectif avant le calcul
requestAnimationFrame(function() {
    document.dispatchEvent(new CustomEvent('acgr:refresh-scroll-all'));
});
Toujours utiliser requestAnimationFrame avant le dispatch — sans lui, clientWidth peut encore être 0 si le browser n'a pas encore appliqué le changement de classe CSS.

4. API référence

Contrat entièrement revu depuis la v3.12.0 Une version antérieure de cette section décrivait un endpoint unique piloté par un champ action dans un POST JSON. Ce n'est plus le contrat par défaut : la grille appelle désormais 5 routes distinctes, avec de vraies méthodes HTTP. Si votre backend suit encore l'ancien schéma à endpoint unique (ou tout autre schéma), cfg.routes permet de le brancher tel quel — voir Adapter à un backend existant en fin de section. Aucune obligation de migrer un backend qui fonctionne déjà.

La grille délègue tout au réseau via cfg.routes, avec ces valeurs par défaut :

ActionRoute par défautDétail
lireGET {url}?...Liste, pagination, recherche, filtres
sauvegarderPOST {url} (création) ou PUT {url}/{id} (modification)Corps JSON = champs de la ligne
supprimerDELETE {url}/{id}
options_cascadeGET {url}/cascade/{champ}?valeur_parent=...Options d'un select dépendant
exporterGET {url}/export?...Mêmes filtres que lire

Query params de lire — précisions importantes

GET {url}?acgr_id=...&start=0&length=25&search=...&order_tris=...&filtres_colonnes=...

Trois paramètres sont des chaînes JSON, pas des tableaux natifs :

ParamètreContenu décodé
order_tris[{"col":0,"sens":"asc"}] — index de colonne + sens
filtres_colonnes{"nom":"dup"} — filtre par colonne, clé = nom du champ
epinglees["12","45"] — ids des lignes épinglées (absent si aucune)

Réponse attendue

{
  "data":             [ { "id": 1, "nom": "Dupont", "prenom": "Marie" }, ... ],
  "recordsFiltered":  1234,
  "epinglees":        [ { "id": 5, "nom": "Martin", ... }, ... ],
  "totaux_generaux":  { "montant": 128450 }
}
ChampRequisDescription
dataouiPage de données courante (sans les épinglées)
recordsFilterednonNombre total de lignes filtrées ; sans lui, la grille utilise data.length
epingleesnonDonnées complètes des lignes épinglées PRO
totaux_generauxnonUn agrégat déjà calculé par champ — voir l'avertissement ci-dessous
totaux_generaux : le calcul est intégralement à votre charge Contrairement à ac_datagrid.php — où la classe construit elle-même le SQL et traduit agregat:'moyenne' en AVG()ac_datagrid_pro.js ne calcule jamais rien côté serveur, par nature (backend agnostique). La grille affiche tel quel totaux_generaux[champ], avec le préfixe correspondant à col.agregat. Si vous déclarez agregat:'moyenne' sur une colonne mais que votre backend renvoie une somme SQL brute, la grille affichera cette somme avec un devant, sans aucune erreur — elle n'a pas les données brutes sous les yeux pour vérifier la cohérence. Faites correspondre le calcul SQL/ORM à l'agregat déclaré sur chaque colonne.

Contrat d'API — sauvegarder

// Création — POST {url}
// Corps : { "nom": "Martin", "prenom": "Jean" }
// Réponse : { "id": 157 }

// Modification — PUT {url}/157
// Corps : { "nom": "Martin", "prenom": "Jean-Noël" }
// Réponse : {} (corps vide accepté, le code HTTP fait foi)

L'id retourné à la création est indispensable : c'est lui qui remplace le row_id négatif temporaire de la ligne fraîchement créée côté client.

Contrat d'API — supprimer

// DELETE {url}/157
// Réponse : {} (ou 204 No Content)

Le corps n'est pas inspecté, seul le code HTTP compte.

Contrat d'API — options_cascade

// GET {url}/cascade/id_statut?valeur_parent=3
// Réponse : { "options": [ { "id": 1, "libelle": "Actif" }, { "id": 2, "libelle": "Inactif" } ] }

Gestion des erreurs

Le code HTTP fait foi : tout code < 400 est un succès, tout code ≥ 400 un échec.

{ "message": "Explication lisible", "code": "VALIDATION_ERREUR" }

message est affiché à l'utilisateur ; code est transmis dans l'événement acgr:erreur (e.detail.code, absent si non fourni).

Piège Laravel n°1 — pagination Model::paginate($length) ne renvoie PAS la forme attendue — il renvoie {data, current_page, total, per_page, ...}. Reconstruisez la réponse à la main : {'data' => $page->items(), 'recordsFiltered' => $page->total()}.
Piège Laravel n°2 — validation $request->validate([...]) renvoie nativement 422 {"message":"...", "errors":{...}} — la grille ne lit que .message (générique), .errors est ignoré. Attrapez l'exception et aplatissez le message vous-même si vous voulez remonter le détail par champ.

Adapter à un backend existant

Chaque entrée de cfg.routes accepte construire et/ou mapper, indépendamment — utile pour brancher la grille sur un backend qui suit d'autres conventions, y compris l'ancien schéma à endpoint unique décrit plus haut :

new AcDatagridPro({
    url: '/api/donnees.php',
    routes: {
        lire: {
            construire(params) {
                return { method: 'POST', url: '/api/donnees.php',
                         body: { action: 'load', page: Math.floor(params.start / params.length) + 1,
                                 per_page: params.length, search: params.search } };
            },
            mapper(json) {
                return { ok: json.status === 'ok', data: json.data,
                         recordsFiltered: json.total, totaux_generaux: json.totals };
            },
        },
        // ... sauvegarder / supprimer / options_cascade / exporter, même principe
    },
    colonnes: [ ... ]
});

Pas d'authentification/CSRF intégrée pour ce protocole (stateless) : branchez la vôtre via cfg.enTetes(action, params), appelé à chaque requête, qui retourne les en-têtes HTTP à fusionner (typiquement Authorization: Bearer ...).

Feuille de route

Saut de version 1.1.0 → 3.12.0 Depuis la v3.12.0, ac_datagrid_pro est compilée depuis les mêmes sources que ac_datagrid.php via un script de build commun — les deux classes portent désormais le même numéro de version. Le tableau ci-dessous reste l'historique réel de la ligne 1.x ; la ligne 3.12.0 marque la bascule architecturale, pas une régression de version.
FeatureStatutVersion
Constructeur + presets✅ Livré1.0.0.e-alpha
Tri multi-colonnes (Shift+clic)✅ Livré1.0.0.e-alpha
Pagination serverside/clientside✅ Livré1.0.0.e-alpha
Recherche globale + filtres auto-filter✅ Livré1.0.0.e-alpha
Cookies (persistance état)✅ Livré1.0.0.e-alpha
CRUD modal + cell✅ Livré1.0.0.e-alpha
CRUD inline✅ Livré1.0.0.e-alpha
Resize colonnes✅ Livré1.0.0.e-alpha
Épingles ★ (persistantes toutes pages)✅ Livré1.0.0.e-alpha
Totaux + formatage conditionnel✅ Livré1.0.0.e-alpha
4 thèmes CSS embarqués✅ Livré1.0.0.e-alpha
Mode statique data: []✅ Livré1.0.0.e-alpha
Export CSV / XLSX✅ Livré1.0.0.e-alpha
Import CSV (mapping visuel 3 étapes)✅ Livré1.0.0-beta
Menu contextuel colonnes (⋮)✅ Livré1.0.0.g-alpha
Sous-grilles imbriquées✅ Livré1.0.0.g-alpha
Colonnes fixées gauche/droite (⋮)✅ Livré1.0.0.h-alpha
Colonne expand ▶ sticky✅ Livré1.0.0.h-alpha
Duplication de ligne (⧉)✅ Livré1.0.0-beta
Debounce recherche globale (400ms)✅ Livré1.0.0.i-alpha
Sous-grille hors table maître (scroll indépendant)✅ Livré1.0.0-beta
Cookies sous-grilles (cookieKey stable par URL)✅ Livré1.0.0-beta
Sauvegarde nb lignes/page dans cookies✅ Livré1.0.0-beta
Détection automatique mode client/serveur❌ Retirée — voir Quick Start1.0.3-beta → absente en 3.12.0
Scrollbar horizontale miroir (auto, synchronisée)✅ Livré1.0.5
Hooks publics JS : acgrRefreshScroll / acgr:refresh-scroll-all✅ Livré1.0.5
Accessibilité ARIA (grid/row/gridcell, aria-live, aria-expanded...)✅ Livré1.1.0
Widgets externes brancheables (setValeur/getValeur)✅ Livré1.1.0
Suppression groupée des lignes épinglées✅ Livré1.1.0
Événements acgr:log, acgr:charger-sous-grille, acgr:apres-suppression-multiple✅ Livré1.1.0
Fusion du noyau avec ac_datagrid.php (build.php commun, transport REST configurable via cfg.routes)✅ Livré3.12.0
Presets (mini/recherche/edition/edition_avancee/complet)✅ Livré3.12.0
format_conditionnel multi-règles (plusieurs paliers de couleur)✅ Livré3.12.0
epingle_page_1 (optimisation réseau des lignes épinglées)✅ Livré3.12.0
Événement acgr:apres-chargement, e.detail.row sur acgr:row-click✅ Livré3.12.0
Sous-grilles déclaratives (sous_grilles: [...] dans la config du parent)✅ Livré3.12.0
AbortController — destroy() annule une requête en vol✅ Livré3.12.0
Adaptateur Laravel✅ Livré (démo)3.12.0
Correctif 'defaut' de colonne (ignoré côté JS depuis v3.6.7, sans effet réel jusqu'à cette version)✅ Livré3.13.0
placeholder, aide (édition + en-tête), min/max/step, maxlength — colonnes✅ Livré3.13.0
decimales — plafond d'affichage sans zéro forcé, découplé de monetaire✅ Livré3.13.0
defaut_actif — active réellement 'defaut' sur un filtre externe, persistance localStorage✅ Livré3.13.0
Événement acgr:before_delete (annulable, avant tout appel réseau, suppression simple et groupée)✅ Livré3.13.0
acgr:log enrichi de detail.donnees✅ Livré3.13.0
pk — clé primaire explicite, prioritaire sur l'heuristique getPK(), exclue d'office du corps envoyé au backend REST✅ Livré3.13.0
editable:'creation' — saisissable uniquement à la création, verrouillé et visible en modification✅ Livré3.13.0
formule — colonnes calculées, recalcul live navigateur, whitelist anti-injection✅ Livré3.14.0
Adaptateur Symfony📋 Planifié
Publication npm📋 Planifié