ac_datagrid v3.14.0

Grille de données PHP/JS vanilla — zéro dépendance — monofichier
PHP 8.0+MySQL / MariaDBZéro dépendance JS WCAG AAi18nPDO préparéCSRF

Architecture v3.12.0 — noyau commun avec ac_datagrid_pro

Depuis la v3.12.0, ac_datagrid partage son noyau JavaScript (rendu, tri, pagination, édition, export, sous-grilles…) avec ac_datagrid_pro, la version JS pure destinée aux projets sans backend PHP (Laravel, Symfony, API REST quelconque). Les deux classes sont compilées depuis les mêmes sources partagées via un script de build unique ; seul le transport réseau diffère (dispatcher PHP intégré ici, contrat REST configurable côté Pro).

Concrètement pour vous : toute nouvelle fonctionnalité du noyau (formatage conditionnel, presets, agrégats…) est disponible dans les deux classes simultanément, et documentée ici comme avant — cette page ne couvre que ac_datagrid. Pour l'usage JS pur sans backend PHP, voir la documentation dédiée ac_datagrid_pro (contrat REST, mode données en mémoire, presets par défaut différents).

Introduction

ac_datagrid est un composant PHP/JavaScript qui génère des grilles de données interactives à partir d'une simple configuration PHP. Un seul fichier à déposer dans votre projet — aucune dépendance npm, composer, jQuery ou CDN.

Il couvre l'intégralité du cycle CRUD : lecture paginée, ajout, édition (inline, modale ou cellule par cellule), suppression, export XLSX et import CSV. Deux modes de rendu sont disponibles : clientside (toutes les données chargées en une fois, tri/recherche/filtres instantanés côté navigateur, recommandé jusqu'à ~5 000 lignes) et serverside (pagination et filtrage côté serveur, testé à 200 000 lignes en grille principale et 2 000 000 en sous-grilles, gère facilement 2 000 000 de lignes).

La classe gère elle-même la connexion PDO, la sécurité CSRF, la session PHP et la génération complète du HTML/CSS/JS — la page appelante n'a qu'à instancier et afficher. Elle est conçue pour être déployée par simple copie FTP, sans aucune étape de build.

Fonctionnalités principales

ac_datagrid est un composant commercial développé et maintenu par artisan-code.fr. Zéro dépendance, monofichier, déployable par simple copie FTP.

Clientside ou serverside ?

CritèreClientsideServerside
Volume recommandéJusqu'à ~5 000 lignesAu-delà de 5 000 lignes
Réactivité (tri, recherche)Instantanée — zéro AJAXAller-retour serveur à chaque action
Charge serveur1 requête SQL au chargement1 requête par page/filtre/tri
Données exposéesTable entière dans le navigateurSeulement la page visible
Configurationserverside: false (défaut)serverside: true

Règles importantes d'intégration

Ces règles ont été établies lors d'une intégration en production sur un produit en prod (janvier–mars 2026).

Historique des versions

VersionDateApports principaux
v02023ac_grille_data_table.php — jQuery + DataTables, 1483 lignes
v12024Réécriture vanilla JS zéro dépendance, 1854 lignes
v2.02024CSRF, cookies, colonnes avancées, filtres auto, totaux — 3233 lignes
v2.12024Refactoring JS 9 méthodes, corrections
v3.02025edit_mode:cell (double-clic inline), duplication de ligne
v3.12025Import CSV 3 étapes (FileReader API)
v3.22025Suppression groupée des lignes épinglées
v3.32025Formatage conditionnel numérique, miniatures images avec lightbox
v3.42025Accessibilité WCAG 2.1 AA — ARIA, navigation clavier
v3.5.0202545 tests unitaires, documentation API HTML + ODT
v3.5.12025Correction ajout_mode + coloration ligne en édition
v3.5.22025Event acgr:row-click, sélection visuelle de ligne
v3.5.32025e.detail simplifié à {id, grille_id, selected}
v3.5.42025Corrections read_only — initModeCell, étoile, suppression groupée
v3.5.52025Suppression outline sur .acgr-tr-selected
v3.5.62025Suppression double outline mode cell
v3.5.7Mars 2026Filtres externes de type date (date_debut / date_fin)
v3.5.8Mars 2026Debounce recherche serverside, registre window.acGrilleInstances, subgrid_param séparé
v3.5.9Mars 2026masquerColonne / reafficherColonne appellent charger() en serverside
v3.5.10Mars 2026Correction focus perdu à la frappe en clientside, touche Échap pour vider la recherche
v3.5.11Mars 2026Option vide «—» en tête des selects en modale d'ajout
v3.5.12Mars 2026Scroll vers la grille fille après row-click (setTimeout 100ms)
v3.5.13Mars 2026Formatage conditionnel date, miniatures images avec lightbox, duplication configurable
v3.5.14Mars 2026castValeur() retourne null pour select FK vide (fix Incorrect integer value)
v3.5.15Avril 2026Correction boucle ajouterLigne mode inline (PK écrasée → mauvais événement émis)
v3.5.16Avril 2026Migration page Classothèque ac_datagrid — bandeau formatage conditionnel
v3.5.17Avril 2026Correction cookies : filtres auto et recherche globale non sauvegardés
v3.5.18Avril 2026Fix transparence colonnes fixées (sticky) sur lignes sélectionnées/en édition
v3.5.19Avril 2026Selects en cascade : pré-filtrage automatique à l'ouverture de l'édition + sélection de la valeur courante (tentative non fonctionnelle, corrigée en v3.7.5)
v3.5.20Avril 2026Cookies : sauvegarde et restauration du nombre de lignes par page ; restauration correcte des largeurs de colonnes (table-layout:fixed après lock des largeurs auto) ; bouton ↺ visible si nb lignes modifié
v3.5.21Avril 2026Cascade en mode cell (chargement AJAX des options filtrées au double-clic si col.parent défini) ; nouvelle ligne toujours en tête de page 1 (ajout + duplication) ; pageCourante = 1 forcé après INSERT modal
v3.6.0Avril 2026Export XLSX natif (remplace ODS) — même API, fichier .xlsx compatible Excel et LibreOffice ; option epingle:false pour masquer la colonne étoile (ignoré en read_only) ; tri multi-colonnes par Shift+clic avec indicateurs visuels ▲▼ et numéro de priorité, persistance cookie ; cascade en mode cell (AJAX si col.parent) ; nouvelle ligne en tête page 1 (ajout + duplication) ; pageCourante forcé après INSERT modal
v3.6.2Avril 2026Option insert:false sur une colonne — empêche son insertion en BDD (utile pour les colonnes FK issues d'un JOIN utilisées comme parent de cascade)
v3.6.3Avril 2026Préférences grille (tri, colonnes masquées, largeurs, pagination) migrées de cookies vers localStorage — élimine les erreurs 400 Bad Request liées au dépassement de limite du header Cookie (~8 KB Apache)
v3.6.4Avril 2026Option edit_mode:'none' — grille sans aucune édition, sans bouton Valider/Annuler ; bloque nativement toute tentative de sauvegarde
v3.6.5Avril 2026Mode cell : blur sur select et checkbox déclenche la validation immédiate (cohérent avec text/number/date/textarea)
v3.6.6Mai 2026Largeurs de colonnes déclarées en configuration prises en compte dès l'initialisation (mode table-layout:auto respecte les style.width posés sur chaque <th>). Persistance des largeurs dans le localStorage désactivée pour contourner un bug d'interaction navigateur/CSS sur les grilles sans colonne sticky. Le drag de redimensionnement reste fonctionnel pendant la session ; les autres préférences (ordre, masquage, filtres, tris, recherche, pagination) restent persistées.
v3.6.7Mai 2026Bug fix : longueur de pagination. Lors d'un changement de configuration (longueur_defaut), le localStorage périmé n'écrase plus la nouvelle valeur de configuration. La config est désormais prioritaire si elle a changé depuis la dernière session. Les préférences utilisateur (localStorage) ne sont restaurées que si la config n'a pas changé.
v3.6.8Mai 2026Scrollbar horizontale miroir au-dessus de la grille — synchronisée bidirectionnellement, affichée automatiquement seulement en cas de débordement horizontal. Colonne Actions masquée automatiquement si aucun bouton ne peut y apparaître (edit_mode:'none' + duplication:false + non admin). Hooks publics JS : méthode container.acgrRefreshScroll(), événement ciblé acgr:refresh-scroll et événement global acgr:refresh-scroll-all pour recalculer la scrollbar miroir après affichage d'un onglet caché.
v3.7.0Mai 2026Event acgr:log dispatché sur document après chaque écriture BDD confirmée (CREATE/UPDATE/DELETE). Permet aux projets hôtes de logger les actions sans modifier la classe. La clé table doit être présente dans la config JS. Les projets qui n'écoutent pas cet event ne sont pas perturbés.
v3.7.1Mai 2026Option total:false par colonne — exclut une colonne number des totaux automatiques (utile pour les champs numériques non sommables, ex : une année). Patch PHP (SUM serverside), buildConfigJS (transmission PHP→JS) et buildTfoot (calcul JS).
v3.7.2Juin 2026Symbole Σ/∑ toujours visible en pied de grille. Quand la colonne étoile est absente (epingle:false ou read_only:true), le symbole se loge dans la première colonne non-totalisée (l’ID en pratique) via un flag labelPose dans buildLigneTotaux. Aucune régression sur les grilles avec épinglage actif.
v3.7.3Juillet 2026Mode lecture d'une colonne checkbox : affichage d'une vraie case à cocher désactivée (au lieu du texte brut 0/1), avec pointer-events:none pour que le double-clic traverse jusqu'au <td> et garde la cellule éditable. Comparaison de valeur cohérente entre lecture et édition (val est une string issue du JSON).
v3.7.4Juillet 2026Séparation des listes d'options d'une colonne select : options (alimentée en priorité par sql_options) pour le <select> d'édition/création, et options_display (alimentée en priorité par sql_options_display) pour résoudre le libellé d'une valeur déjà enregistrée en lecture. Permet un dropdown de saisie filtré (ex : masquer des valeurs standards) tout en gardant l'affichage des lignes existantes correct. Rétrocompatible : si une seule requête est fournie, les deux listes retombent dessus (comportement identique aux versions antérieures) ; si les deux requêtes sont identiques, elles ne sont exécutées qu'une fois.
v3.7.5Juillet 2026Correction du pré-remplissage des selects en cascade (parent + sql_options avec :parent) à l'ouverture de l'édition en edit_mode:'inline'. La tentative précédente comparait la valeur à resélectionner avec input.value, relu sur un <select> qui vient d'être créé sans ses vraies options (elles dépendent du parent et ne sont jamais chargées à l'avance) — la comparaison se faisait donc toujours contre une chaîne vide, et aucune option n'était jamais présélectionnée. Le correctif utilise directement ligne[col.champ] (la valeur réelle transmise par le serveur) et cible uniquement le select concerné, sans recharger à tort les colonnes soeurs partageant le même parent. Même principe que la cascade en mode cell (v3.5.21), qui n'a jamais eu ce défaut. Aucun changement de config requis côté page.
v3.7.6Juillet 2026Correction d'un affichage intermittent d'ID brut au lieu du libellé, juste après l'ajout d'une ligne référençant une FK créée après le chargement de la page (ex : une inscription à un cours ajoutée dans la même session, puis une participation saisie pour ce même bénéficiaire sans recharger). Cause : options_display est chargée une seule fois au rendu PHP de la page, contrairement à options d'une colonne cascade, rechargée en direct via AJAX à chaque édition — la valeur toute fraîche est donc absente de la liste figée. Le correctif mémorise le libellé affiché du <select> au moment de la saisie (validerLigne()) et l'ajoute à options_display après succès de l'écriture (nouvelle fonction enrichirOptionsDisplay()), avant le re-rendu de la ligne. Transparent pour toutes les grilles existantes, aucun changement de config requis côté page.
v3.8.0Juillet 2026Nouvelle option show_nbr_lines (défaut true) — masque à la source le sélecteur "Afficher X lignes" quand false, ni le <div class="acgr-select-wrap"> ni sa règle CSS ne sont générés. Remplace un contournement CSS !important côté projet hôte devenu inutile. Rétrocompatible : absent de la config, comportement inchangé. Refonte du bandeau read_only : texte par défaut plus explicite (« Consultation — ce tableau n'est pas modifiable avec votre rôle. » au lieu de « Consultation uniquement — modifications désactivées. »), icône 👁 au lieu de 🔒, style neutre piloté par les variables de thème (--acgr-thead-hover, --acgr-border, --acgr-text-muted) au lieu d'un jaune/orange d'alerte codé en dur, et largeur inline-flex au lieu de pleine largeur — pour ne plus laisser penser que toute la page est bloquée. read_only_msg reste surchargeable comme avant.
v3.9.0Juillet 2026Sélection à fort volume pour les colonnes select : nouvelle clé sql_options_ajax (recherche AJAX à partir de 2 caractères, LIMIT 50 côté serveur) et nouvelle clé champ_libelle (le libellé est fourni par une jointure dans le SQL principal plutôt que par une liste options_display préchargée). Nouvelle action AJAX options_recherche. Les widgets Classothèque peuvent désormais s'activer en mode inline, pas seulement modal : chaque widget déclare sa propre propriété statique MODES_SUPPORTES (repli sur ['modal'] si absente — aucun changement de comportement pour les widgets existants qui ne la déclarent pas). Nouveau widget Classothèque : ac_big_select (voir section Widgets). Design strictement additif : toutes les nouvelles clés sont optionnelles et absentes par défaut, zéro impact sur les grilles existantes.
v3.9.1Juillet 2026Correction d'un bug préexistant (antérieur à cette version, invisible tant qu'ajout_mode:'modal' n'était pas utilisé sans widget) : la branche modale de l'ajout de ligne écrasait l'ID temporaire négatif de la clé primaire par sa valeur defaut en itérant toutes les colonnes, faute d'exclure explicitement la PK de cette boucle — contrairement à la branche inline, qui a toujours eu cette garde. Conséquence sur les tables dont l'injection de champs système dépend d'un ID strictement négatif : l'INSERT échouait par violation de clé étrangère. Corrigé en excluant la PK de la boucle d'initialisation, dans la branche modale comme dans la branche inline.
v3.9.2Juillet 2026Nouveau type de colonne datetime, pour les colonnes DATETIME en base (à ne plus déclarer en date, qui tronque silencieusement toute valeur porteuse d'une heure). Rendu en <input type="datetime-local"> (sans les secondes). Le filtre automatique de colonne compare par jour (DATE(champ) côté serveur), l'heure n'entre pas en compte dans le filtrage. Additif : aucune colonne date existante n'est affectée.
v3.9.3Juillet 2026Affichage des colonnes de type date au format jj/mm/aaaa en lecture.
v3.9.4Juillet 2026Dédoublonnage de masquerColonne(), reafficherColonne() et ouvrirPanneauColonnes() — deux définitions concurrentes du même nom coexistaient, JavaScript ne conservait que la dernière déclarée. Ajout du préfixe cfg.id sur les id de checkbox du panneau de colonnes masquées, pour éviter toute collision entre grilles sur une même page.
v3.10.0Août 2026Passage à une architecture de développement src/ + build.php : le code source est désormais réparti en fichiers PHP/JS/CSS distincts, recompilés en un seul fichier ac_datagrid.php par un script de build local (aucun changement pour les pages hôtes, qui continuent d'inclure un fichier unique sans dépendance). Fusion de render() et renderServerside() en une seule fonction render(), ce qui corrige au passage un oubli : les widgets inline (ex. ac_big_select) et la resynchronisation de la scrollbar miroir du haut, jusqu'ici actifs uniquement en mode client, le sont désormais aussi en serverside.
v3.10.1Août 2026Fusion des branches modale et inline de ajouterLigne() en une fonction construireNouvelleLigne() commune. Corrige au passage : la valeur par défaut d'une colonne (option defaut) n'était appliquée qu'en ajout_mode:'modal', jamais en ajout_mode:'inline' — c'est désormais le cas dans les deux modes. Corrige aussi un bug de focus : après un ajout inline, le curseur ne se plaçait jamais dans le premier champ de la nouvelle ligne (parenthèses d'appel manquantes dans le setTimeout).
v3.10.2Août 2026Deux chantiers. (1) build.php compacte désormais intégralement le fichier livrable : retrait de tous les commentaires PHP/JS/CSS, aucun retour à la ligne dans le JS, le CSS ni le PHP (les nowdoc sont convertis en chaînes PHP simple-quote au moment du build), bannière de fichier généré en tête, et une option --dev pour produire à la place un build lisible et diagnostiquable en sandbox. Aucun changement fonctionnel : les sorties générées à l'exécution sont identiques à l'octet près. (2) Dédoublonnage de validerChampsRequis() (partagée entre l'ajout modal et l'ajout inline, comportement de surlignage en modale préservé) et de attacherEcouteursCell() (partagée entre les cellules select et checkbox en edit_mode:'cell') — ce second dédoublonnage a révélé puis corrigé un bug d'édition introduit lors du même chantier : une structure if/else mal reconstituée faisait que toute cellule en édition affichait la valeur brute au lieu du libellé pour une colonne select, quel que soit son type.
v3.11.0Août 2026Cinq chantiers, tous testés unitairement (batterie tests/run.php, 12 tests) et rétrocompatibles par défaut. (1) Export CSV : nouvelle clé export_formats (défaut ['xlsx'] — comportement inchangé pour toute grille existante) ; un tableau à 2 entrées transforme le bouton unique en menu déroulant. (2) Action custom : bouton optionnel dans la colonne Actions (action_custom, défaut false), tire l'événement acgr:action-custom avec {id, ligne, table, grille_id} — fonctionne même en read_only. (3) Agrégats multiples : col.agregat (somme | moyenne | min | max | nombre, défaut somme) remplace le SUM unique des totaux, en serverside (SQL) comme en clientside (JS) ; préfixe visuel par cellule (⌀ ▼ ▲ #) uniquement pour les agrégats non par défaut. (4) Hook serveur on_avant_sauvegarde : callable exécuté juste avant l'INSERT/UPDATE, reçoit tous les champs (y compris invisibles/FK), doit retourner le tableau, peut bloquer via throw new RuntimeException() — voir section Hooks serveur PHP. (5) Types email / url / tel / color : pattern de validation par défaut (overridable via col.pattern), ignoré si un widget est configuré sur la colonne. Correctif associé : validerChampsRequis() s'applique désormais aussi en edit_mode:'cell' (jusqu'ici seul le contrôle serveur s'appliquait dans ce mode — amélioration UX, aucun changement sur ce qui peut être enregistré).
v3.12.0Août 2026Fusion architecturale avec ac_datagrid_pro — voir Architecture v3.12.0 en tête de document. Le noyau JS (rendu, tri, pagination, édition, export, sous-grilles) est désormais partagé entre les deux classes via build.php --target=php|pro|both ; seul le transport réseau diffère. Nouveautés du noyau, disponibles dans les deux classes : (1) preset — raccourci de config (mini/recherche/edition/edition_avancee/complet), défaut complet pour ac_datagrid (tableau vide, comportement historique garanti inchangé par construction). (2) format_conditionnel multi-règles — accepte désormais un tableau de règles ordonnées en plus de la forme historique à seuil unique, pour gérer plus de deux couleurs. (3) epingle_page_1 — n'envoie les identifiants épinglés au serveur qu'en page 1 (optimisation réseau, comportement d'affichage inchangé). (4) Événement acgr:apres-chargement — émis après chaque chargement réussi (clientside et serverside), avec {total, page, grille_id}. (5) e.detail.row ajouté à l'événement acgr:row-click (objet complet de la ligne, en plus de id). Toutes ces additions sont strictement rétrocompatibles : absentes de la config, comportement inchangé à l'octet près (vérifié par tests dédiés dans tests/run.php, 17 tests).
v3.13.0Août 2026Rétrocompatible par défaut — aucune grille existante en prod n'est affectée sans configuration explicite. Certains produits en prod restent volontairement sur une version antérieure ; ces nouveautés ne sont déployées que progressivement, produit par produit. Correctifs : (1) defaut de colonne — l'option était lue côté JS depuis v3.6.7 mais jamais transmise par buildConfigJS(), donc sans aucun effet réel jusqu'à cette version. (2) Collision du registre window.acGrilleInstances entre sous-grilles — indexation désormais sur cfg.id (unique par instance), cfg.acgr_id conservé en alias rétrocompatible. Confort de saisie (colonne) : placeholder, aide (édition + en-tête), min/max/step (number), maxlength (text/textarea). decimales (colonne, number) — plafond de décimales à l'affichage sans zéro forcé, découplé de monetaire depuis cette version (applicable à toute colonne number). defaut_actif (grille) — active réellement la valeur defaut d'un filtre externe au premier chargement, avec persistance localStorage. Hooks serveur : on_avant_suppression (nouveau, bloque une suppression avec message) ; on_log enrichi d'un 4e paramètre $donnees. Événements JS : acgr:before_delete (nouveau, annulable via preventDefault(), suppression simple et groupée) ; acgr:log enrichi de detail.donnees. pk (colonne) — clé primaire explicite, prioritaire sur l'heuristique editable:false, exclue d'office de l'écriture PHP même sans editable:false. editable:'creation' (colonne) — saisissable uniquement à la création (inline, cellule, modale, duplication), verrouillé et visible (libellé résolu pour un select) en modification, requis ignoré hors création.
v3.14.0Août 2026Rétrocompatible par défaut — aucune grille existante en prod n'est affectée, la clé formule n'ayant jamais existé avant cette version. formule (colonne) — colonne calculée, ex. '({prix_ht} * {tva}) * {nbr_articles}' (syntaxe type tableur, {champ} référence toute autre colonne de la même ligne). Force editable:false automatiquement (aucune double déclaration nécessaire), dans ac_datagrid.php comme dans ac_datagrid_pro. Recalculée en direct côté navigateur à chaque saisie d'un champ source (inline, modale, duplication) ; valeur vide tant qu'un champ source manque ou n'est pas numérique — jamais 0 par défaut. Recalculée et écrite côté serveur, de façon autoritaire, à chaque INSERT/UPDATE pour ac_datagrid.php (jamais lue depuis $_POST, même en cas de requête forgée) ; pour ac_datagrid_pro avec un backend externe, c'est la valeur calculée côté navigateur qui est transmise, le développeur du backend restant responsable de son propre calcul serveur s'il le juge nécessaire. Évaluation protégée par une whitelist stricte (chiffres et opérateurs arithmétiques uniquement après substitution des {champ}) — aucune exécution de code arbitraire possible, côté PHP (evaluerFormule()) comme côté JS (_evaluerFormule()). Le chaînage entre colonnes calculées (une formule référençant une autre formule) n'est pas supporté dans cette version, prévu pour la 4.0.0.

Getting Started — grille fonctionnelle en 5 minutes

1. Copier ac_datagrid.php dans votre projet

Un seul fichier à déposer. Aucune dépendance npm, composer ou CDN.

2. Créer une table MySQL exemple

CREATE TABLE produits (
    id      INT AUTO_INCREMENT PRIMARY KEY,
    nom     VARCHAR(100) NOT NULL,
    prix    DECIMAL(10,2),
    actif   TINYINT(1) DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3. Page PHP minimale

<?php
require_once 'ac_datagrid.php';

$cfg = [
    // Connexion
    'db_host'     => 'localhost',
    'db_port'     => '3306',
    'db_name'     => 'ma_base',
    'db_user'     => 'user',
    'db_password' => 'secret',

    // Identification
    'id'    => 'grille_produits',
    'table' => 'produits',
    'sql'   => 'SELECT id, nom, prix, actif FROM produits',

    // Colonnes
    'colonnes' => [
        ['champ'=>'id',    'titre'=>'ID',    'editable'=>false],
        ['champ'=>'nom',   'titre'=>'Nom',   'type'=>'text',     'requis'=>true],
        ['champ'=>'prix',  'titre'=>'Prix',  'type'=>'number',   'monetaire'=>true],
        ['champ'=>'actif', 'titre'=>'Actif', 'type'=>'checkbox'],
    ],
];

$g = new ac_datagrid($cfg);

// Intercepter les requêtes AJAX
if ($g->estRequeteAjax()) {
    $g->traiterAjax();
    exit;
}
?>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"></head><body>
<?= $g->genereCSS() ?>
<?= $g->genereHTML() ?>
<?= $g->genereJS() ?>
</body></html>
C'est tout. La grille est fonctionnelle avec lecture, ajout, édition et suppression. La connexion PDO, le token CSRF et la pagination sont gérés automatiquement.

Gestion AJAX — pattern recommandé

Toutes les opérations (lire, sauvegarder, supprimer, exporter…) passent par des requêtes POST vers la même page PHP. Le pattern recommandé :

if ($g->estRequeteAjax()) {
    $g->traiterAjax();
    exit;
}

// Variante multi-grilles sur la même page :
if (isset($_POST['acgr_id'])) {
    if (strpos($_POST['acgr_id'], 'praticiens_') === 0) {
        $g = new ac_datagrid(array_merge($dbCfg, getConfigPraticiens()));
        $g->traiterAjax(); exit;
    }
    if (strpos($_POST['acgr_id'], 'patients_') === 0) {
        $g = new ac_datagrid(array_merge($dbCfg, getConfigPatients()));
        $g->traiterAjax(); exit;
    }
}
Important : en mode AJAX multi-grilles, n'appelez pas getConfigPraticiens() si cette fonction charge des options via PDO — cela ouvre une connexion inutile. Utilisez une config allégée sans sql_options.

Actions AJAX disponibles

acgr_actionDescriptionCSRF requis
lireLecture des données (GET-like)Non
sauvegarderINSERT (row_id < 0) ou UPDATE (row_id > 0)Oui
supprimerDELETE par clé primaireOui
options_cascadeRechargement options select dépendantNon
options_rechercheRecherche AJAX pour colonne à fort volume (sql_options_ajax) v3.9.0Non
exporterRetourne les données pour export XLSXNon

Format de réponse unifié

// Succès
{ "ok": true, "message": "Enregistrement ajouté.", "code": "INSERT_OK", "id": 42 }

// Échec
{ "ok": false, "message": "Le champ «Nom» est obligatoire.", "code": "CHAMP_REQUIS" }

Options générales

OptionTypeDéfautDescription
idstringautoIdentifiant unique de la grille (recommandé : le fixer manuellement)
db_hoststringlocalhostHôte MySQL
db_portstring3306Port MySQL
db_name requisstringNom de la base de données
db_user requisstringUtilisateur MySQL
db_password requisstringMot de passe MySQL
db_charsetstringutf8mb4Charset connexion PDO
table requisstringNom de la table pour INSERT/UPDATE/DELETE
sql requisstringRequête SELECT (peut être un SELECT complexe avec JOIN)
edit_modestringinlineinline | modal | cell
ajout_modestring=edit_modeinline | modal — mode spécifique pour l'ajout (utile avec edit_mode='cell')
modal_titrestring=idTitre affiché dans la fenêtre modale
ajoutbooltrueAfficher le bouton d'ajout
label_ajouterstring+ AjouterTexte du bouton d'ajout
deleteboolfalseActiver la suppression (passe le rôle en 'admin')
read_onlyboolfalseMode consultation uniquement, aucune modification. Affiche un bandeau discret (icône 👁, style neutre) v3.8.0
read_only_msgstringautoMessage affiché en mode lecture seule. Défaut depuis v3.8.0 : « Consultation — ce tableau n'est pas modifiable avec votre rôle. »
show_action_buttonsbooltrueAfficher la colonne Actions
duplicationboolfalseBouton ⧉ pour dupliquer une ligne
import_csvboolfalseBouton d'import CSV (3 étapes)
csv_separateurstring;Séparateur CSV : ; ou ,
recherchebooltrueChamp de recherche globale
paginationbooltrueActiver la pagination
longueur_defautint25Nombre de lignes par page par défaut
show_nbr_linesbooltruefalse = masque à la source le sélecteur "Afficher X lignes" (ni généré, ni sa règle CSS) v3.8.0
ordre_defautarray[0,'asc']Colonne et sens de tri initial : [indexColonne, 'asc'|'desc']
filtre_autobooltrueLigne de filtres sous les en-têtes
totauxbooltrueLigne de totaux pour les colonnes number
symbole_monnaiestringSymbole monétaire global
serversideboolfalsePagination/tri/filtrage côté serveur (recommandé > 5 000 lignes)
exportboolfalseBouton export
export_filenamestringexportNom du fichier exporté (sans extension)
export_formatsarray['xlsx']Formats proposés : 'xlsx' et/ou 'csv'. Un seul format = clic direct (comportement inchangé) ; deux formats = menu déroulant v3.11.0
action_customboolfalseBouton supplémentaire dans la colonne Actions, tire acgr:action-custom — voir section Événements JS v3.11.0
action_custom_iconestringGlyphe du bouton d'action custom (comme pour dupliquer) v3.11.0
action_custom_labelstringnullBulle d'aide du bouton d'action custom. null = libellé générique L('action_custom') v3.11.0
epinglebooltrueAfficher la colonne étoile d'épinglage (ignoré si read_only:true)
epingle_page_1boolfalsetrue = n'envoie les identifiants épinglés au serveur qu'en page 1 (optimisation réseau — le rendu, lui, a toujours ignoré les épinglées hors page 1, ce paramètre ne change que ce qui est transmis) v3.12.0
cookiesbooltrueMémoriser les préférences (ordre, colonnes masquées/fixées, filtres, tris, recherche, pagination) en localStorage
debugboolfalseAfficher les réponses non-JSON du serveur dans un panneau dédié
labelsarray[]Surcharge des labels d'interface (voir section i18n)
presetstring|nullnullRaccourci pré-remplissant plusieurs options d'un coup (voir tableau ci-dessous). null = comportement identique à son absence v3.12.0
defaut_actifboolfalsefalse (défaut) = comportement d'origine inchangé : la clé defaut d'un filtre externe (voir section Filtres externes) reste posée en HTML mais ignorée côté JS au premier chargement. true = le defaut s'applique réellement, persiste en localStorage, et resynchronise le select visible avec la valeur restaurée v3.13.0
on_avant_suppressioncallablenullfunction(int $rowId, array $ligne): void|string — appelé juste avant le DELETE. Retourner une chaîne non vide bloque la suppression (message d'erreur côté client). Voir section Hooks serveur PHP v3.13.0

Presets disponibles

Un preset pré-remplit un jeu cohérent d'options ; toute option explicitement fournie dans la config l'emporte toujours sur le preset, même si le preset la définit aussi. 'complet' est un tableau vide dans le code source — c'est ce qui garantit qu'il ne peut jamais diverger des défauts de la classe : ne pas préciser preset du tout revient exactement au même.

ValeurUsage typique
miniGrille minimale lecture seule, clientside, sans recherche ni filtres
recherchemini + recherche, filtres, cookies
editionCRUD en modale, serverside
edition_avanceeedition + édition cellule par cellule, ajout inline, épingles
complet (défaut)Comportement standard de la classe, aucune restriction
v3.8.0 — show_nbr_lines. Jusqu'ici, seuls filtre_auto et export permettaient de masquer un élément de la barre d'outils selon le contexte (ex : une vue simplifiée par rôle) — le sélecteur "Afficher X lignes" n'avait pas d'équivalent, obligeant les projets hôtes à le masquer en CSS. Passer show_nbr_lines => false évite désormais ce contournement : le <div class="acgr-select-wrap"> et sa règle CSS ne sont simplement pas générés. Absent de la config, comportement strictement inchangé (rétrocompatible).
Hooks serveur (callables). Deux clés de config acceptent un callable PHP et ne figurent pas dans le tableau ci-dessus (qui ne couvre que les options scalaires/array) : on_log et on_avant_sauvegarde. Voir la section dédiée Hooks serveur PHP.

Configuration des colonnes

Chaque élément du tableau colonnes est un tableau associatif :

CléTypeDéfautDescription
champ requisstringNom du champ SQL retourné par la requête
titrestring=champEn-tête de colonne affiché
typestringtexttext | number | date | datetime | checkbox | select | textarea | email | url | tel | color email/url/tel/color v3.11.0
editablebool|stringtruefalse = colonne non modifiable (sert aussi à définir la clé primaire par heuristique). 'creation' = saisissable uniquement à la création (inline, cellule, modale, duplication) ; verrouillé et visible en modification — voir la note ci-dessous 'creation' v3.13.0
pkboolfalsetrue = désigne explicitement cette colonne comme clé primaire, prioritaire sur l'heuristique editable:false. Exclue d'office de l'écriture PHP (INSERT et UPDATE) même si elle n'est pas editable:false — protection contre un POST forgé qui tenterait de modifier la clé 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 (pas de double déclaration). Recalculée en direct côté navigateur à la saisie des champs sources, et recalculée côté serveur à chaque INSERT/UPDATE — voir la note ci-dessous v3.14.0
requisboolfalseChamp obligatoire — validé côté PHP et JS
visiblebooltruefalse = champ caché, utilisé pour les FK parentes
largeurstringautoLargeur CSS : '120px', '10%'
champ_tablestring=champNom réel du champ en base si différent de l'alias SQL
monetaireboolfalseAffichage formaté avec séparateur milliers et symbole
symbolestring=symbole_monnaieSymbole monétaire spécifique à cette colonne
totalbooltruefalse = exclut cette colonne des lignes de totaux (utile pour un champ number sémantiquement non sommable, ex : une année) v3.7.1
agregatstringsommesomme | moyenne | min | max | nombre — fonction appliquée aux totaux de cette colonne (total reste le simple interrupteur d'affichage). Préfixe visuel ⌀ ▼ ▲ # par cellule pour tout agrégat non par défaut v3.11.0
patternstringnullRegex (sans délimiteurs) qui override le pattern par défaut d'un type email/url/tel. Sans effet si widget est configuré sur la colonne v3.11.0
dupliquerbooltruefalse = champ non copié lors d'une duplication de ligne
format_conditionnelarraynullColoration selon valeur (voir section dédiée)
url_imageboolfalseAfficher la valeur comme vignette image cliquable
image_taillestring40pxTaille de la vignette (carré)
image_roundstringnoneround | rounded | square | none
widgetstringnullNom de la classe Classothèque (ex: 'ac_carte'). Active en modale toujours, et en inline si la classe déclare static MODES_SUPPORTES incluant 'inline' v3.9.0
widget_ancrestringdivinput (ac_telephone) | div (ac_carte)
widget_optionsarray[]Options passées au constructeur du widget
defautmixednullValeur par défaut à la création d'une nouvelle ligne. Corrigé en v3.13.0 : l'option était documentée et lue côté JS depuis v3.6.7 (construireNouvelleLigne()), mais jamais transmise par buildConfigJS() — sans aucun effet réel jusqu'à cette version, quelle que soit la config v3.13.0
champ_libellestringnullColonne à fort volume : nom du champ (jointure SQL) fournissant le libellé en lecture, lu en priorité avant tout repli sur options/options_display v3.9.0
sql_options_ajaxstringnullColonne à fort volume : requête de recherche AJAX avec marqueur :terme, exécutée à la demande (seuil 2 caractères, LIMIT 50) plutôt que de précharger toutes les options v3.9.0
placeholderstringnullTexte indicatif dans le champ vide (attribut placeholder natif). Édition inline, modal et cellule ; ignoré sur select et checkbox v3.13.0
aidestringnullBulle d'aide (attribut title). Affichée sur le champ de saisie en édition ET sur l'en-tête de colonne (survol, hors édition) — pas de bulle par cellule en lecture, jugé trop bruyant sur une grille avec beaucoup de lignes v3.13.0
minnumbernullBorne minimale native (attribut min), colonnes type:'number' uniquement v3.13.0
maxnumbernullBorne maximale native (attribut max), colonnes type:'number' uniquement v3.13.0
stepnumbernullPas d'incrément natif (attribut step), colonnes type:'number' uniquement v3.13.0
maxlengthintnullLongueur maximale de saisie (attribut maxlength natif), colonnes type:'text' et type:'textarea' v3.13.0
decimalesintnullnull (défaut) = comportement d'origine strictement inchangé (colonne monetaire:true : 2 décimales fixes ; colonne number non monétaire : valeur brute, sans formatage). Posé : devient un plafond de décimales, sans zéro forcé — 5 reste 5, 5,25 reste 5,25 avec decimales:2, jamais 5,00. S'applique à toute colonne type:'number', avec ou sans monetaire:true — découplé de monetaire depuis cette version v3.13.0
v3.9.0 — colonnes à fort volume. Pour un select référençant des milliers de lignes (ex : sélection d'un bénéficiaire parmi plusieurs milliers), précharger options/options_display devient coûteux. Déclarer champ_libelle + sql_options_ajax à la place : la classe ne précharge plus rien (sauf en edit_mode:'cell', seul mode qui garde besoin du préchargement complet), le libellé de lecture vient d'une jointure déjà présente dans le SQL principal, et la recherche d'édition interroge le serveur à la demande via la nouvelle action AJAX options_recherche. Voir le widget ac_big_select dans la section Widgets, conçu pour ce cas d'usage.
editable: 'creation' — v3.13.0. Un champ saisissable seulement à la création, verrouillé ensuite — typiquement une référence, un numéro de dossier, ou tout champ qui ne doit plus bouger une fois la ligne créée.
  • Ligne nouvelle (inline, cellule, modale, duplication) : traité comme editable:true, saisissable normalement.
  • Ligne existante : non éditable en inline ni en cellule (double-clic sans effet). En modale, le champ reste visible mais verrouillé — un input disabled affichant la valeur (libellé résolu pour un select, comme en lecture inline), sans attribut data-champ : il n'est donc jamais soumis au serveur.
  • requis sur un champ 'creation' n'est validé qu'à la création, côté client et côté serveur — sinon une modification échouerait systématiquement puisque le champ verrouillé n'est pas soumis.
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, pas besoin de le déclarer soi-même.
  • 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 édition inline, en modale, et immédiatement à l'ouverture d'une ligne dupliquée (les sources copiées déclenchent un calcul initial, pas seulement les saisies suivantes).
  • Champ source manquant ou non numérique → la colonne calculée reste vide, jamais 0 par défaut.
  • Autorité du calcul serveur — pour ac_datagrid.php, la valeur n'est jamais lue depuis $_POST : elle est recalculée côté PHP à chaque INSERT/UPDATE à partir des valeurs déjà castées des autres colonnes, protection contre toute requête forgée. Pour ac_datagrid_pro avec un backend externe (Laravel, Node…), c'est la valeur calculée côté navigateur qui est envoyée telle quelle — la classe n'a pas de code serveur à patcher dans ce cas, un développeur souhaitant une double vérification doit l'implémenter dans son propre backend.
  • Sécurité — 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, côté PHP comme côté JS : aucune exécution de code arbitraire possible.
  • 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.

Identifier la clé primaire

Si une colonne porte pk => true, c'est elle. Sinon, la clé primaire est la première colonne avec editable => false et visible !== false (heuristique inchangée). Si aucune colonne ne correspond, le fallback est 'id'.

['champ' => 'id', 'titre' => 'ID', 'editable' => false]  // <- clé primaire (heuristique)
['champ' => 'uuid', 'titre' => 'ID', 'pk' => true, 'editable' => false]  // <- clé primaire (explicite, prioritaire)

Types de colonnes

text

Champ texte simple. Rendu : <input type="text"> en édition.

number

Valeur numérique. Rendu : <input type="number">. Participe aux totaux automatiques. Utiliser total => false pour exclure un champ non sommable (ex : une année). Compatible monetaire et format_conditionnel.

date

Date au format ISO YYYY-MM-DD. Rendu : <input type="date">. Compatible format_conditionnel.

datetime v3.9.2

Date et heure, colonne DATETIME en base. Rendu : <input type="datetime-local"> (sans les secondes — un input datetime-local ne les gère pas nativement). Compatible format_conditionnel (même logique de comparaison que date).

Ne pas déclarer une colonne DATETIME en type => 'date'. Un <input type="date"> n'accepte que le format strict YYYY-MM-DD : lui assigner une valeur porteuse d'une heure ("2026-06-28 14:30:00") est silencieusement rejeté par le navigateur, laissant le champ vide — en lecture comme en écriture, sans erreur JS ni PHP. Toujours utiliser type => 'datetime' pour une colonne DATETIME.
Filtre automatique de colonne. Une colonne datetime réutilise le même <input type="date"> que le filtre auto d'une colonne date : le filtrage se fait par jour, l'heure est ignorée. Côté serveur, la comparaison SQL porte sur DATE(champ) plutôt que sur champ directement.

checkbox

Booléen stocké en TINYINT(1). Rendu : case à cocher. Le filtre automatique propose Oui/Non/Tous.

select

Liste déroulante. Nécessite options (tableau PHP) ou sql_options (requête SQL) :

// Options statiques
['champ' => 'statut', 'type' => 'select', 'options' => [
    ['id' => 1, 'libelle' => 'Actif'],
    ['id' => 2, 'libelle' => 'Inactif'],
]]

// Options dynamiques depuis la BDD
['champ' => 'id_ville', 'type' => 'select',
 'sql_options'         => 'SELECT id_ville AS id, libelle FROM villes ORDER BY libelle',
 'sql_options_display' => 'SELECT id_ville AS id, libelle FROM villes ORDER BY libelle']

// Select en cascade (dépend d'un autre select)
['champ' => 'id_sous_cat', 'type' => 'select',
 'parent'       => 'id_categorie',     // champ parent
 'parent_label' => 'la catégorie',
 'sql_options'  => 'SELECT id, libelle FROM sous_categories WHERE id_cat = :parent']
v3.7.4 — deux listes distinctes. sql_options alimente la liste options qui remplit le <select> d'édition/création (et sert de base à la cascade AJAX). sql_options_display alimente la liste options_display qui sert uniquement à résoudre le libellé d'une valeur déjà enregistrée en lecture. Cette séparation permet un dropdown de saisie filtré (ex : n'afficher que des valeurs personnalisées) tout en gardant l'affichage correct des lignes existantes qui utilisent une valeur exclue du dropdown. Si une seule des deux requêtes est fournie, l'autre retombe dessus (comportement identique aux versions ≤ 3.7.3). Si les deux requêtes sont identiques, elles ne sont exécutées qu'une seule fois.
Cascade : un select avec parent recharge ses options en AJAX au changement du parent. sql_options_display (toutes les options sans filtre :parent) reste nécessaire pour afficher le libellé correct en lecture sans avoir à recharger. Depuis v3.7.5, le pré-remplissage à l'ouverture de l'édition (edit_mode:'inline') sélectionne correctement la valeur déjà enregistrée, en s'appuyant directement sur la donnée de la ligne plutôt que sur l'état du DOM.
v3.7.6 — fraîcheur d'options_display. options_display est chargée une seule fois au rendu PHP de la page. Une ligne créée référençant une FK apparue après ce chargement, dans la même session, affichait donc son ID brut plutôt que le libellé résolu (bug intermittent : un simple F5 corrigeait toujours l'affichage). Depuis v3.7.6, la classe mémorise le libellé affiché du <select> au moment de la saisie et l'ajoute à options_display juste après le succès de l'écriture, avant le re-rendu de la ligne — transparent pour toute grille existante.

textarea

Zone de texte multi-lignes. Rendu tronqué à 100 caractères en affichage. Occupe toute la largeur de la modal (class acgr-modal-full).

email, url, tel, color v3.11.0

Quatre types dédiés, chacun avec un <input type="..."> HTML natif (clavier mobile adapté, sélecteur natif pour color) et, pour email/url/tel, un pattern de validation appliqué à la sauvegarde (inline, modal et mode cellule).

Typeinput.typePattern de repli (si non vide)
emailemail^[^\s@]+@[^\s@]+\.[^\s@]+$
urlurl^https?://.+\..+
teltel^[+]?[0-9 .\-()]{6,20}$ — générique, aucun pays présumé
colorcoloraucun — le sélecteur natif ne produit que du valide
['champ' => 'email',     'titre' => 'Email',     'type' => 'email'],
['champ' => 'site_web',  'titre' => 'Site web',  'type' => 'url'],
['champ' => 'telephone', 'titre' => 'Téléphone', 'type' => 'tel'],
['champ' => 'couleur',   'titre' => 'Couleur',   'type' => 'color'],

// Pattern personnalisé — remplace le pattern par défaut du type
['champ' => 'telephone_fr', 'titre' => 'Tél. FR', 'type' => 'tel',
 'pattern' => '^0[1-9][0-9]{8}$']
Le contrôle de format ne s'applique que si le champ est non vide — un champ facultatif vide n'est jamais rejeté — et jamais si widget est configuré sur la colonne : le widget (ex. ac_telephone_multipays) est alors seul responsable de sa propre validation, pour éviter qu'un pattern générique rejette à tort une valeur produite dans un format spécifique au widget.

Modes d'édition

inline (défaut)

L'édition se fait directement dans la ligne du tableau. Les cellules se transforment en champs de formulaire. Boutons Valider / Annuler en colonne Actions.

modal

Une fenêtre modale s'ouvre avec tous les champs. Supporte les widgets Classothèque. Fermeture par Echap ou clic sur le fond.

'edit_mode'   => 'modal',
'modal_titre' => 'Mon enregistrement',

cell

Double-clic sur une cellule pour l'éditer individuellement. L'ajout de nouvelles lignes utilise ajout_mode (inline ou modal). Navigation clavier : Enter / F2 pour éditer, Échap pour annuler, ↑↓←→ pour se déplacer.

'edit_mode'  => 'cell',
'ajout_mode' => 'modal',  // modal recommandé en mode cell
En mode cell, les widgets Classothèque ne s'activent que dans la modal d'ajout. Le double-clic sur les colonnes avec widget bascule vers un simple input.

Filtres externes

Des selects de filtrage peuvent être placés au-dessus de la grille. Ils déclenchent un rechargement AJAX.

'defaut_actif' => true,   // v3.13.0 — nécessaire pour que 'defaut' ci-dessous s'applique réellement
'filtres' => [
    [
        'id'          => 'filtre_ville',
        'label'       => 'Ville',
        'champ_sql'   => 'id_ville',   // champ utilisé dans le WHERE
        'sql_options' => 'SELECT id_ville AS id, libelle FROM villes ORDER BY libelle',
        'defaut'      => null,          // valeur sélectionnée par défaut
    ],
],
defaut_actif — v3.13.0. La clé defaut d'un filtre pose bien l'attribut selected en HTML, mais seule l'option defaut_actif:true (au niveau grille) fait réellement passer cette valeur dans les requêtes AJAX dès le premier chargement, et la fait persister en localStorage. Sans defaut_actif:true, defaut reste cosmétique : le select affiche la bonne valeur, mais la grille charge sans filtre au premier chargement. Une grille qui compensait ce manque avec un dispatchEvent(new Event('change')) manuel côté page doit retirer ce contournement en activant defaut_actif — sinon double appel réseau au chargement.

Serverside

En mode serverside, seule la page courante est chargée depuis le serveur. La pagination, le tri, la recherche et les filtres sont exécutés en SQL.

'serverside'      => true,
'longueur_defaut' => 25,    // lignes par page
'ordre_defaut'    => [0, 'asc'],

Quand l'utiliser ?

Totaux généraux serverside

En mode serverside avec totaux => true, deux lignes de totaux sont affichées : Σ (page courante) et (total général sur toutes les données filtrées, calculé côté serveur).

En serverside, la recherche globale et les filtres colonnes sont exécutés en SQL. Les colonnes filtrables sont celles retournées par la requête SQL. Assurez-vous que les colonnes filtrées sont indexées pour les performances.

Sous-grilles

Une grille peut contenir des sous-grilles qui se chargent au clic sur ▶. Chaque ligne de la grille maître peut avoir sa propre sous-grille.

// 1. Créer la sous-grille avec subgrid=true et subgrid_param
$grPat = new ac_datagrid(array_merge($dbCfg, [
    'id'            => 'patients',
    'table'         => 'patients',
    'sql'           => 'SELECT id, nom, id_praticien FROM patients',
    'subgrid'       => true,
    'subgrid_param' => 'id_praticien',  // colonne de liaison
    'colonnes'      => [
        ['champ'=>'id',           'editable'=>false],
        ['champ'=>'nom',          'type'=>'text'],
        ['champ'=>'id_praticien', 'visible'=>false, 'editable'=>false],
    ],
]));

// 2. Attacher à la grille maître
$grPrat = new ac_datagrid(array_merge($dbCfg, [
    'id'            => 'praticiens',
    'subgrid'       => true,
    'subgrid_param' => 'id_praticien',
    'sous_grilles'  => [$grPat],
    // ...
]));
Le paramètre subgrid_param doit correspondre à un champ de la table parent qui sera transmis comme filtre à la sous-grille. La colonne correspondante dans la sous-grille doit avoir visible => false.

Performance

Les sous-grilles sont chargées à la demande (lazy loading). Testé à 2 000 000 de lignes réparties dans les sous-grilles de 200 000 lignes parentes.

Export XLSX / CSV

L'export génère un fichier directement dans le navigateur, sans dépendance serveur. Il respecte les filtres actifs au moment de l'export.

// Comportement historique — inchangé, bouton unique, clic direct
'export'          => true,
'export_filename' => 'mon_export',  // nom sans extension → fichier mon_export_YYYY-MM-DD.xlsx

// v3.11.0 — CSV en plus, bouton devient un menu déroulant
'export'          => true,
'export_formats'  => ['xlsx', 'csv'],
'export_filename' => 'mon_export',
Compatibilité. export_formats vaut ['xlsx'] par défaut — toute grille existante qui ne déclare pas cette clé garde un bouton unique et un clic direct, identiques à l'octet près au comportement antérieur à la v3.11.0. Le menu déroulant n'apparaît que si export_formats contient plus d'un format.

XLSX

Le fichier généré comporte une ligne d'en-têtes en gras et les données. Les valeurs numériques sont typées comme nombres. Compatible Excel 2010+ et LibreOffice Calc.

CSV v3.11.0

Séparateur : csv_separateur (même clé que pour l'import CSV, cohérence garantie). Échappement RFC4180 (guillemets si virgule/guillemet/retour-ligne dans une valeur). BOM UTF-8 en tête du fichier — indispensable pour qu'Excel FR affiche correctement les caractères accentués.

'csv_separateur' => ';',  // ou ','

Import CSV

L'import se fait en 3 étapes via une modale : sélection du fichier → mapping des colonnes → prévisualisation + import.

'import_csv'    => true,
'csv_separateur' => ';',  // ou ','

Étapes

  1. Fichier — glisser-déposer ou sélection. Lecture via FileReader API (côté client).
  2. Mapping — association automatique par nom de colonne (insensible à la casse), ajustable manuellement.
  3. Prévisualisation — aperçu des 5 premières lignes. Import ligne par ligne avec barre de progression et rapport d'erreurs.
L'import utilise le même endpoint AJAX sauvegarder que l'ajout manuel. Les validations côté serveur s'appliquent (champs requis, types). Les lignes invalides sont ignorées et listées dans le rapport.

Formatage conditionnel

Colorise le texte d'une cellule selon la valeur. Fonctionne sur les colonnes number et date.

Colonne numérique

['champ' => 'tarif', 'type' => 'number',
 'format_conditionnel' => [
    'seuil'     => 60,          // valeur de référence (float)
    'superieur' => '#dc2626',  // rouge si tarif > 60
    'egal'      => '#f59e0b',  // orange si tarif = 60
    'inferieur' => '#16a34a',  // vert si tarif < 60
]]

Colonne date

['champ' => 'date_installation', 'type' => 'date',
 'format_conditionnel' => [
    'seuil'     => '2020-01-01',  // date de référence ISO
    'superieur' => '#16a34a',    // vert si date > 2020
    'egal'      => '#f59e0b',    // orange si date = 2020
    'inferieur' => '#dc2626',    // rouge si date < 2020
]]
Les clés superieur, egal, inferieur sont toutes optionnelles. Seules les clés présentes sont appliquées.

Plusieurs paliers v3.12.0

Pour plus de deux couleurs (ex. vert / orange / rouge), format_conditionnel accepte aussi un tableau de règles, évaluées dans l'ordre — la première règle qui correspond gagne. Les deux formes sont détectées automatiquement (is_array avec clés numériques vs seuil/superieur/egal/inferieur) ; l'ancienne forme reste strictement inchangée, aucune migration nécessaire.

['champ' => 'note', 'type' => 'number',
 'format_conditionnel' => [
    ['operateur' => '>=', 'valeur' => 8.5, 'couleur' => '#16a34a', 'gras' => true],
    ['operateur' => '>=', 'valeur' => 7.6, 'couleur' => '#f59e0b'],
    ['operateur' => '<',  'valeur' => 7.6, 'couleur' => '#dc2626'],
]]
Clé de la règleDescription
operateur> >= < <= = (ou ==)
valeurValeur de comparaison (nombre, ou date ISO si la colonne est type => 'date'/'datetime')
couleurCouleur CSS appliquée si la règle correspond
grasbool, optionnel — met le texte en gras en plus de la couleur

Vignettes images

Une colonne peut afficher une URL comme vignette cliquable (lightbox au clic).

['champ'       => 'photo',
 'titre'       => 'Photo',
 'type'        => 'text',
 'url_image'   => true,
 'image_taille'=> '40px',      // taille du carré affiché
 'image_round' => 'round',     // round | rounded | square | none
]

Si l'URL est invalide ou l'image inaccessible, un placeholder ? est affiché. Le clic ouvre un lightbox plein écran avec fermeture par Échap ou clic.

Widgets Classothèque

Chaque widget déclare sa propre static MODES_SUPPORTES (ex : ['modal', 'inline']). Un widget qui ne la déclare pas garde le comportement historique — modal uniquement, input texte simple en inline ou cell — repli qui protège tous les widgets existants sans modification v3.9.0.

// ac_carte — saisie d'adresse avec carte interactive
['champ'          => 'adresse',
 'type'           => 'text',
 'widget'         => 'ac_carte',
 'widget_ancre'   => 'div',          // ancre de type div
 'widget_options' => [
    'mode'            => 'saisie',
    'hauteur'         => '280px',
    'lien_itineraire' => false,
]]

// ac_telephone_multipays — saisie de numéro avec indicatif
['champ'          => 'telephone',
 'type'           => 'text',
 'widget'         => 'ac_telephone_multipays',
 'widget_ancre'   => 'input',          // ancre de type input
 'widget_options' => ['pays_defaut' => 'FR']]

// ac_big_select — sélection à fort volume, recherche AJAX (v3.9.0)
['champ'            => 'id_beneficiaire',
 'type'             => 'select',
 'widget'           => 'ac_big_select',
 'champ_libelle'    => 'lib_beneficiaire',   // fourni par une jointure du SQL principal
 'sql_options_ajax' => 'SELECT id_beneficiaire AS id, CONCAT(nom, " ", prenom) AS libelle
                         FROM dat_beneficiaires
                         WHERE id_asso = :id_asso AND actif = 1
                           AND CONCAT(nom, " ", prenom) LIKE :terme
                         ORDER BY nom, prenom',
 'sql_options'      => 'SELECT id_beneficiaire AS id, CONCAT(nom, " ", prenom) AS libelle
                         FROM dat_beneficiaires WHERE id_asso = :id_asso']
ac_big_select (v1.0.0, Classothèque) — widget pour <select> à fort volume (des milliers d'options). Recherche déclenchée à partir de 2 caractères (anti-rebond 250 ms, jeton de requête pour ignorer les réponses obsolètes), navigation clavier (flèches, Entrée, Échap), bouton d'effacement pour revenir en recherche. static MODES_SUPPORTES = ['modal', 'inline'] — actif dans ces deux modes ; en edit_mode:'cell', le <select> natif reste utilisé (par choix, pas par limite technique). Zéro dépendance externe.

widget_ancre

Les widgets doivent exposer les méthodes getValeur() et setValeur(id, libelle) de la Classothèque (getLibelle() également, pour les widgets à liste comme ac_big_select).

Scrollbar horizontale miroir v3.6.8

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 une grille est dans un onglet avec display:none au chargement, son tableWrap.clientWidth est 0 à ce moment-là et le calcul reste à zéro. Il faut déclencher un recalcul une fois l'onglet visible. Voir la section Hooks publics JS ci-dessous.

i18n — Internationalisation

Toutes les chaînes d'interface sont externalisées et remplaçables via l'option labels.

Usage

$g = new ac_datagrid([
    // ...
    'labels' => [
        'enregistrer'    => 'Save',
        'annuler'        => 'Cancel',
        'aucun_resultat' => 'No results',
        // Les clés non fournies gardent la valeur FR par défaut
    ],
]);

Labels disponibles (sélection)

CléDéfaut FRDescription
afficherAfficherSélecteur nb lignes
ligneslignesSélecteur nb lignes
rechercherRechercher :Label du champ de recherche
aucun_resultatAucun résultatInfo pagination vide
aucune_donneeAucune donnée disponibleTableau vide
enregistrerEnregistrerBouton modal
annulerAnnulerBouton annuler
validerValiderBouton valider (inline)
editerÉditerBouton éditer
supprimerSupprimerBouton supprimer
dupliquerDupliquer cette ligneTooltip bouton ⧉
nouveau✦ NouveauBadge modal ajout
edition✎ ÉditionBadge modal édition
champ_requis_jsLe champ «%s» est obligatoire.Validation JS (%s = nom du champ)
champ_requisLe champ «%s» est obligatoire.Validation PHP côté serveur
insert_okEnregistrement ajouté.Message PHP succès INSERT
update_okModifications enregistrées.Message PHP succès UPDATE
delete_okEnregistrement supprimé.Message PHP succès DELETE
erreur_reseauImpossible de contacter le serveur :Erreur réseau JS

La liste complète des ~65 clés est disponible dans $LABELS_DEFAUT en haut du fichier ac_datagrid.php.

Méthode statique

// Utiliser un label en PHP (pour les messages serveur)
$msg = ac_datagrid::lblStatic($labels, 'insert_ok');
$msg = ac_datagrid::lblStatic($labels, 'champ_requis', 'Nom');  // avec sprintf

Événements JavaScript

La grille émet des événements DOM sur son élément acgr-wrap-{id}. Ils remontent (bubbles).

acgr:row-click
Émis au clic sur une ligne (hors boutons/inputs). Permet de piloter d'autres composants depuis la sélection. La ligne sélectionnée reçoit la classe acgr-tr-selected (fond coloré). Un second clic la désélectionne.
wrap.addEventListener('acgr:row-click', function(e) {
    // e.detail.id        : id de la ligne cliquée
    // e.detail.grille_id : id de la grille émettrice
    // e.detail.selected  : true = sélectionnée, false = désélectionnée
    // e.detail.row       : objet complet de la ligne (v3.12.0)
    if (!e.detail.selected) return;
    chargerSousGrilles(e.detail.id);
});

// Cas typique en prod — une grille pilote 4 sous-grilles
wrap.addEventListener('acgr:row-click', function(e) {
    if (!e.detail.selected) return;
    var id = e.detail.id;
    chargerParcoursPro(id);
    chargerAteliers(id);
    chargerFamille(id);
    chargerAdministratif(id);
});
Non émis si une cellule est en cours d'édition (mode cell), ni sur les lignes nouvelles (id < 0).
acgr:apres-chargement v3.12.0
Émis après chaque chargement réussi (initial, pagination, tri, recherche, filtre — clientside comme serverside). Utile pour un chrono de performance, un spinner externe, ou de l'analytics.
wrap.addEventListener('acgr:apres-chargement', function(e) {
    // e.detail.total     : nombre total de lignes (filtrées)
    // e.detail.page      : page courante
    // e.detail.grille_id : id de la grille émettrice
    console.log(e.detail.total + ' lignes, page ' + e.detail.page);
});
Correction (05/08/2026) — Cette section documentait auparavant deux événements (acgr:avant-suppression et acgr:avant-suppression-multiple) absents du code actuel. La confirmation de suppression (unitaire ou groupée) passe directement par AcNotifModale.confirmer() (ou confirm() natif en repli), appelé en interne — jamais par un événement DOM annulable que la page hôte devrait écouter.
acgr:apres-insert
Émis après un INSERT réussi.
// e.detail.donnees   : objet avec les valeurs insérées
// e.detail.grille_id : id de la grille
acgr:apres-update
Émis après un UPDATE réussi.
// e.detail.donnees   : objet avec les valeurs mises à jour
// e.detail.grille_id : id de la grille
acgr:apres-suppression
Émis après un DELETE réussi.
// e.detail.id        : id supprimé
// e.detail.grille_id : id de la grille
acgr:apres-suppression-multiple
Émis après la suppression groupée des lignes épinglées (bouton dédié de la barre d'outils).
// e.detail.ids       : tous les ids tentés
// e.detail.nb_ok     : nombre de suppressions réussies
// e.detail.erreurs   : [{id, message}] pour les échecs
// e.detail.grille_id : id de la grille
acgr:erreur
Émis sur toute erreur (réseau, serveur, validation).
// e.detail.message : message lisible
// e.detail.code    : code machine (CHAMP_REQUIS, RUNTIME_ERROR…)
wrap.addEventListener('acgr:erreur', e => afficherErreur(e.detail.message));
acgr:action-custom v3.11.0
Émis au clic sur le bouton d'action custom de la colonne Actions (visible seulement si action_custom => true). La grille ne fait rien d'autre que dispatcher l'événement — à charge de la page appelante de le traiter (ouvrir une modale de détail, générer un document, etc.). Fonctionne même en read_only.
wrap.addEventListener('acgr:action-custom', function(e) {
    // e.detail.id        : clé primaire de la ligne
    // e.detail.ligne      : objet complet de la ligne (tous les champs)
    // e.detail.table      : nom de la table (cfg.table)
    // e.detail.grille_id  : id de la grille émettrice
    ouvrirDetail(e.detail.id, e.detail.ligne);
});
Le bouton est désactivé pendant l'édition d'une autre ligne (même mécanisme que le bouton ⧉ dupliquer) et n'apparaît jamais sur une ligne en cours de création (id < 0).
acgr:log v3.7.0
Dispatché sur document (pas sur le conteneur) après chaque écriture BDD confirmée (CREATE, UPDATE, DELETE). Permet aux projets hôtes de logger les actions sans modifier la classe. Les projets qui n'écoutent pas cet événement ne sont pas perturbés.
// Dispatché sur document, pas sur le wrap de la grille
// e.detail.action   : 'CREATE' | 'UPDATE' | 'DELETE'
// e.detail.table    : nom de la table (clé 'table' dans la config JS)
// e.detail.id       : id de la ligne concernée
// e.detail.grille_id : id de la grille émettrice
// e.detail.donnees  : champs de la ligne concernée — v3.13.0

document.addEventListener('acgr:log', function(e) {
    fetch('/assets/php/ac_datagrid_log_ajax.php', {
        method: 'POST',
        body: new URLSearchParams({
            action    : e.detail.action,
            table     : e.detail.table,
            id        : e.detail.id,
            grille_id : e.detail.grille_id,
            csrf_token: insert.csrfToken()
        })
    });
});
La clé table doit être présente dans la configuration PHP de la grille — elle est injectée dans le JS par genereJS(). Pas de token CSRF sur l'endpoint log (le token est consommé par traiterAjax() avant le dispatch). detail.donnees (v3.13.0) est cohérent avec le 4e paramètre du hook PHP on_log — voir section Hooks serveur PHP.
acgr:before_delete v3.13.0
Dispatché sur le conteneur de la grille, avant tout appel serveur, sur suppression simple ET groupée (lignes épinglées). Annulable : appeler event.preventDefault() dans un listener bloque la suppression côté client, sans aller-retour réseau. Pendant JS du hook PHP on_avant_suppression, mais évalué plus tôt (avant même la requête AJAX) et sans accès BDD.
wrap.addEventListener('acgr:before_delete', function(e) {
    // e.detail.id        : clé primaire de la ligne
    // e.detail.ligne     : objet complet de la ligne (ou null si introuvable localement)
    // e.detail.table     : nom de la table (cfg.table)
    // e.detail.grille_id : id de la grille émettrice
    if (e.detail.ligne && e.detail.ligne.statut === 'facture') {
        e.preventDefault();
        AcNotifModale.notif('error', 'Suppression refusée', 'Ligne déjà facturée.', 4);
    }
});
Sur une suppression groupée, une ligne annulée via preventDefault() est simplement sautée — ni comptée en succès, ni en erreur dans acgr:apres-suppression-multiple — et le lot continue avec les suivantes.

Hooks serveur PHP v3.11.0

Trois clés de config acceptent un callable PHP exécuté côté serveur, dans ajaxSauvegarder()/ajaxSupprimer(). Toutes trois sont opt-in : absentes de la config (défaut null), aucun changement de comportement.

on_log

Appelé après chaque écriture BDD confirmée (CREATE, UPDATE, DELETE) — équivalent serveur de l'événement JS acgr:log, utile pour un journal d'audit qui doit survivre même si le JavaScript du navigateur est désactivé ou la page fermée avant que l'événement JS ne soit traité côté client.

'on_log' => function(string $action, int $id, string $table, array $donnees = []) {
    // $action  : 'CREATE' | 'UPDATE' | 'DELETE'
    // $donnees : champs de la ligne concernée — 4e paramètre, v3.13.0
    journaliser($action, $id, $table, $donnees);
},
$donnees — v3.13.0. 4e paramètre ajouté, rétrocompatible : PHP ignore silencieusement les arguments en trop passés à une closure qui en déclare moins, un callback existant à 3 paramètres continue de fonctionner sans modification. Pour DELETE, $donnees contient la ligne telle qu'elle était juste avant suppression (chargée une seule fois, réutilisée pour on_avant_suppression si les deux hooks sont configurés ensemble).

on_avant_sauvegarde

Appelé juste avant l'INSERT/UPDATE, reçoit tous les champs — y compris les colonnes invisibles/FK (visible => false) que la page hôte injecte aujourd'hui souvent manuellement dans $_POST avant traiterAjax(). Le hook sécurise ce cas : la valeur devient injectable côté serveur, non falsifiable côté client.

'on_avant_sauvegarde' => function(array $donnees, bool $estNouveau, ?int $rowId): array {
    // Forçage serveur d'un champ invisible — non falsifiable côté client
    $donnees['id_asso'] = id_asso_courant();

    // Blocage conditionnel — remonte au client via le même mécanisme d'erreur
    // que le token CSRF (rep.message, event acgr:erreur)
    if (!$estNouveau && $donnees['montant'] > 10000) {
        throw new RuntimeException('Montant trop élevé pour une modification.');
    }

    return $donnees;  // toujours retourner le tableau, modifié ou non
},
ParamètreTypeDescription
$donneesarray[champ => valeur], tous les champs éditables + invisibles/FK
$estNouveaubooltrue pour un INSERT, false pour un UPDATE
$rowId?intnull si $estNouveau, sinon l'id de la ligne modifiée
Contrat : le hook doit retourner le tableau $donnees (modifié ou non). Pour bloquer la sauvegarde, lever une RuntimeException — le message est remonté tel quel au client, exactement comme pour la validation CSRF.
Barrière de sécurité. Si le hook ajoute une clé absente du schéma de colonnes déclaré (faute de frappe, champ inventé), elle est silencieusement ignorée — impossible d'injecter une clé SQL arbitraire hors du schéma, même par une erreur d'écriture du hook.

on_avant_suppression v3.13.0

Symétrique de on_avant_sauvegarde, côté suppression. Appelé juste avant le DELETE, reçoit l'id de la ligne et son contenu complet (chargé depuis la vue SQL déclarée). Retourner une chaîne non vide bloque la suppression et remonte le message au client. Ne rien retourner (ou null) autorise la suppression, comportement identique à l'absence du hook.

'on_avant_suppression' => function(int $rowId, array $ligne): void|string {
    if ($ligne['statut'] === 'facture') {
        return 'Impossible de supprimer une ligne déjà facturée.';
    }
    // Rien ou null : suppression autorisée
},
ParamètreTypeDescription
$rowIdintClé primaire de la ligne visée par la suppression
$lignearray[champ => valeur], la ligne telle qu'elle est en base juste avant le DELETE (chargée via un SELECT sur la vue SQL déclarée, filtrée sur la clé primaire)
Contrat : retourner une chaîne non vide bloque la suppression — le message est remonté tel quel au client (rep.message, événement acgr:erreur, code SUPPRESSION_BLOQUEE). Tout le reste (null, chaîne vide, absence de return) autorise la suppression.
Coût. Le SELECT de chargement de $ligne n'est exécuté que si on_avant_suppression et/ou on_log sont configurés sur la grille — aucune requête supplémentaire pour les grilles qui n'utilisent ni l'un ni l'autre, comportement identique à avant cette version dans ce cas.
Pendant côté client. Voir l'événement acgr:before_delete, dispatché plus tôt (avant l'appel serveur), annulable sans aller-retour réseau mais sans accès à l'état réel de la base.

Hooks publics JS v3.6.8

Chaque grille expose trois mécanismes pour déclencher un recalcul de sa scrollbar miroir depuis l'extérieur. Cas d'usage typique : une grille dans un onglet caché (display:none) au chargement. Une fois l'onglet rendu visible, le code appelant notifie la grille pour qu'elle mesure ses vraies dimensions.

Ces hooks sont opt-in — toutes les grilles existantes fonctionnent sans modification.

1. Méthode directe sur le container

Recalcule la scrollbar miroir de cette seule grille.

document.getElementById('acgr-wrap-mon_id').acgrRefreshScroll();

2. Événement DOM ciblé

Même résultat que la méthode directe, mais sous forme d'événement.

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

3. Événement global — toutes les grilles

Dispatché sur documenttoutes les grilles de la page recalculent leur scrollbar. Pratique pour les systèmes d'onglets : un seul appel suffit, quelle que soit la grille concernée. Les grilles encore cachées recalculent aussi mais obtiennent clientWidth = 0 et restent masquées sans effet visible.

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

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

Cookies et personnalisation

La grille mémorise automatiquement les préférences de l'utilisateur :

Note v3.6.7 — La persistance des largeurs de colonnes est temporairement désactivée. Le drag de redimensionnement reste fonctionnel pendant la session courante, mais les largeurs reviennent à leur valeur initiale (config ou auto) à chaque chargement de page. Cette restriction sera levée dans une version ultérieure. Depuis v3.6.7, le localStorage est automatiquement ignoré si la configuration (notamment longueur_defaut) a changé depuis la dernière session — la configuration est prioritaire.

Le cookie est clé par URL + id de grille. Durée : 1 an.

'cookies' => false,  // désactiver complètement

Le bouton ↺ Réinitialiser apparaît dans la toolbar dès qu'une préférence est active. Il supprime le cookie et restaure la configuration initiale.

Mode debug

Activer pour diagnostiquer les réponses serveur non-JSON (erreurs PHP fatales, warnings, pages 500…).

'debug' => true

Lorsqu'une réponse AJAX n'est pas du JSON valide, un panneau s'affiche sous la grille avec :

Ne pas activer en production — le panneau peut afficher des informations sensibles.

Accessibilité WCAG

ac_datagrid implémente les recommandations WCAG 2.1 niveau AA :

Sémantique ARIA

Navigation clavier (mode cell uniquement)

ToucheAction
Tab / Shift+TabNavigation entre éléments interactifs
Enter / F2Entrer en mode édition sur la cellule focusée
EscapeAnnuler l'édition en cours
↑ ↓ ← →Déplacement entre cellules éditables (page courante uniquement)

Focus visible

Tous les éléments interactifs ont un :focus-visible avec outline: 2px solid var(--acgr-accent).

Méthodes publiques

MéthodeRetourDescription
__construct(array $cfg)Instanciation. Démarre la session et génère le token CSRF.
estRequeteAjax(): boolboolRetourne true si la requête POST correspond à cette grille.
traiterAjax(): voidvoidTraite la requête AJAX et envoie la réponse JSON. Appeler exit; après.
genereHTML(): stringstringRetourne le HTML de la grille (conteneur + filtres externes).
genereCSS(string $theme): stringstringRetourne la balise <style> complète. Thèmes : sobre | techno | nature | pastel.
genereJS(): stringstringRetourne la balise <script> avec le moteur JS complet + config.
buildConfigJS(): arrayarrayRetourne la configuration sérialisable en JSON. Utile pour un rebuild dynamique.
getJSCore(): stringstringRetourne le code JS brut (sans balise script, sans config). Utile pour la mise en cache.
getPrimaryKey(): stringstringRetourne le nom du champ clé primaire : la colonne pk:true si posée, sinon la première colonne editable:false et visible, sinon 'id' pk v3.13.0
getSousGrilles(): arrayarrayRetourne le tableau des instances sous-grille.
getVersion(): stringstringStatique. Retourne la version : '3.14.0'.
genererTokenCsrf(): stringstringStatique. Génère ou retourne le token CSRF de session (hex 64 chars).
lblStatic(array $labels, string $cle, ...$args): stringstringStatique. Retourne un label traduit avec support sprintf.

Exemple rebuild dynamique

// PHP — endpoint ?rebuild=1
if (isset($_GET['rebuild'])) {
    $g = new ac_datagrid(array_merge($dbCfg, $maCfg, [
        'edit_mode'  => $_GET['edit_mode']  ?? 'cell',
        'ajout_mode' => $_GET['ajout_mode'] ?? 'modal',
    ]));
    header('Content-Type: application/json');
    echo json_encode([
        'css'  => $g->genereCSS('sobre'),
        'html' => $g->genereHTML(),
        'js'   => $g->genereJS(),
    ]);
    exit;
}

Index des clés — recherche FR/EN v3.12.0

Toutes les clés de configuration sont en français. Si vous cherchez une option en pensant en anglais (habitude naturelle pour qui a déjà utilisé DataTables, AG Grid ou un ORM anglophone), voici la correspondance — Ctrl+F sur le terme anglais pour retrouver directement la bonne clé.

Clé françaiseTermes anglais courants
presetpreset
edit_modeeditMode
ajout_modeinsertMode, addMode
modal_titremodalTitle
ajoutadd, insert, canAdd
label_ajouteraddButtonLabel
read_onlyreadOnly
read_only_msgreadOnlyMessage
show_action_buttonsshowActions, actionsColumn
duplicationduplicate
import_csvimportCsv, csvImport
csv_separateurcsvSeparator, delimiter
recherchesearch
longueur_defautperPage, pageSize, pageLength
show_nbr_linesshowPageSizeSelector, rowsPerPageSelector
ordre_defautdefaultSort, initialSort
filtre_autoautoFilter, columnFilters, filterRow
totauxtotals, showTotals
symbole_monnaiecurrencySymbol
serversideserverSide
export_filenameexportFilename
export_formatsexportFormats
action_customcustomAction
action_custom_iconecustomActionIcon
action_custom_labelcustomActionLabel, customActionTooltip
epinglepinned, pin
epingle_page_1pinnedOnFirstPage
cookieslocalStorage, persistence, remember preferences
labelslabels, i18n, translations

Au niveau colonne :

Clé françaiseTermes anglais courants
champfield
titretitle, label, header
requisrequired
visiblevisible, hidden
largeurwidth
parent_labelparentLabel
widget_optionswidgetOptions
widget_ancrewidgetAnchor, widgetContainer
monetairemonetary, currency
symbolesymbol
dupliquerduplicate (colonne)
format_conditionnelconditionalFormat, conditionalFormatting
url_imageimageUrl
image_tailleimageSize
image_roundimageRound, roundedImage, avatar
agregataggregate, aggregation
patternpattern, regex, validation
options_displayoptionsDisplay, displayOptions
champ_libellelabelField, displayField
recherche_ajaxajaxSearch, remoteSearch, searchAsYouType
pkprimaryKey, isPrimaryKey

ac_datagrid v3.14.0  ·  artisan-code.fr  ·  18 août 2026