ac_datagrid v3.14.0
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
- 2 modes de rendu — clientside (réactif, jusqu'à ~5 000 lignes) et serverside (volumes importants)
- 3 modes d'édition — inline, modale, cellule par cellule (double-clic)
- Sous-grilles — intégrées dans le tableau parent ou séparées sur la page
- Filtres — automatiques par colonne (filtre_auto) et/ou externes avec sql_options
- Export XLSX / CSV / Import CSV — sans dépendance serveur
- i18n — ~65 clés traduisibles, 4 langues démontrées
- Accessibilité WCAG 2.1 AA — ARIA, navigation clavier, focus visible
- 4 thèmes CSS — sobre, techno, nature, pastel — via variables CSS
- Formatage conditionnel — numérique et date
- Événements JS custom — architecture découplée, notifications déléguées
- Registre global —
window.acGrilleInstancespour piloter les grilles depuis JS externe - Cookies — mémorisation des préférences utilisateur (tri multi-colonnes, colonnes masquées, ordre, filtres)
Clientside ou serverside ?
| Critère | Clientside | Serverside |
|---|---|---|
| Volume recommandé | Jusqu'à ~5 000 lignes | Au-delà de 5 000 lignes |
| Réactivité (tri, recherche) | Instantanée — zéro AJAX | Aller-retour serveur à chaque action |
| Charge serveur | 1 requête SQL au chargement | 1 requête par page/filtre/tri |
| Données exposées | Table entière dans le navigateur | Seulement la page visible |
| Configuration | serverside: 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).
-
ob_start() avant tout include en contexte AJAX — toute page gérant à la fois l'affichage HTML
et les requêtes AJAX de la classe doit placer
ob_start()en toute première instruction (avant lesrequire_once) quand$_POST['acgr_id']est présent. Sinon, des warnings PHP ou des whitespaces générés par les includes polluent la réponse JSON.if (isset($_POST['acgr_id'])) { ini_set('display_errors', 0); ob_start(); } // require_once ... viennent ensuite -
champ_sql des filtres = alias du SELECT — la classe enveloppe le SQL dans
SELECT * FROM (...) AS _acgr WHERE .... Dans cette sous-requête, les alias de table (g.id_genre,b.date_inscription) n'existent plus. Utiliser uniquement les alias de colonnes du SELECT (id_genre,date_inscription). -
Colonnes de filtre cachées — si un filtre externe porte sur un champ qui n'est pas
affiché (ex:
id_genrepour filtrer mais affichernom_genre), ajouter ce champ dans le SELECT et le déclarer'visible' => falsedans la config colonnes. -
read_only ne bloque pas l'AJAX lire —
read_only: truebloque uniquementsauvegarderetsupprimer(protégés par CSRF). Le rechargement AJAX des sous-grilles fonctionne normalement en read_only. -
Sous-grilles séparées — pour des grilles enfant affichées séparément sur la page
(pas dans le tableau parent), utiliser
subgrid: true+subgrid_param: 'champ_liaison'et piloter depuis JS viawindow.acGrilleInstances[id].setFiltreParent(champ, valeur)+.charger().
Historique des versions
| Version | Date | Apports principaux |
|---|---|---|
| v0 | 2023 | ac_grille_data_table.php — jQuery + DataTables, 1483 lignes |
| v1 | 2024 | Réécriture vanilla JS zéro dépendance, 1854 lignes |
| v2.0 | 2024 | CSRF, cookies, colonnes avancées, filtres auto, totaux — 3233 lignes |
| v2.1 | 2024 | Refactoring JS 9 méthodes, corrections |
| v3.0 | 2025 | edit_mode:cell (double-clic inline), duplication de ligne |
| v3.1 | 2025 | Import CSV 3 étapes (FileReader API) |
| v3.2 | 2025 | Suppression groupée des lignes épinglées |
| v3.3 | 2025 | Formatage conditionnel numérique, miniatures images avec lightbox |
| v3.4 | 2025 | Accessibilité WCAG 2.1 AA — ARIA, navigation clavier |
| v3.5.0 | 2025 | 45 tests unitaires, documentation API HTML + ODT |
| v3.5.1 | 2025 | Correction ajout_mode + coloration ligne en édition |
| v3.5.2 | 2025 | Event acgr:row-click, sélection visuelle de ligne |
| v3.5.3 | 2025 | e.detail simplifié à {id, grille_id, selected} |
| v3.5.4 | 2025 | Corrections read_only — initModeCell, étoile, suppression groupée |
| v3.5.5 | 2025 | Suppression outline sur .acgr-tr-selected |
| v3.5.6 | 2025 | Suppression double outline mode cell |
| v3.5.7 | Mars 2026 | Filtres externes de type date (date_debut / date_fin) |
| v3.5.8 | Mars 2026 | Debounce recherche serverside, registre window.acGrilleInstances, subgrid_param séparé |
| v3.5.9 | Mars 2026 | masquerColonne / reafficherColonne appellent charger() en serverside |
| v3.5.10 | Mars 2026 | Correction focus perdu à la frappe en clientside, touche Échap pour vider la recherche |
| v3.5.11 | Mars 2026 | Option vide «—» en tête des selects en modale d'ajout |
| v3.5.12 | Mars 2026 | Scroll vers la grille fille après row-click (setTimeout 100ms) |
| v3.5.13 | Mars 2026 | Formatage conditionnel date, miniatures images avec lightbox, duplication configurable |
| v3.5.14 | Mars 2026 | castValeur() retourne null pour select FK vide (fix Incorrect integer value) |
| v3.5.15 | Avril 2026 | Correction boucle ajouterLigne mode inline (PK écrasée → mauvais événement émis) |
| v3.5.16 | Avril 2026 | Migration page Classothèque ac_datagrid — bandeau formatage conditionnel |
| v3.5.17 | Avril 2026 | Correction cookies : filtres auto et recherche globale non sauvegardés |
| v3.5.18 | Avril 2026 | Fix transparence colonnes fixées (sticky) sur lignes sélectionnées/en édition |
| v3.5.19 | Avril 2026 | Selects 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.20 | Avril 2026 | Cookies : 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.21 | Avril 2026 | Cascade 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.0 | Avril 2026 | Export 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.2 | Avril 2026 | Option 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.3 | Avril 2026 | Pré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.4 | Avril 2026 | Option edit_mode:'none' — grille sans aucune édition, sans bouton Valider/Annuler ; bloque nativement toute tentative de sauvegarde |
| v3.6.5 | Avril 2026 | Mode cell : blur sur select et checkbox déclenche la validation immédiate (cohérent avec text/number/date/textarea) |
| v3.6.6 | Mai 2026 | Largeurs 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.7 | Mai 2026 | Bug 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.8 | Mai 2026 | Scrollbar 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.0 | Mai 2026 | Event 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.1 | Mai 2026 | Option 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.2 | Juin 2026 | Symbole Σ/∑ 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.3 | Juillet 2026 | Mode 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.4 | Juillet 2026 | Sé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.5 | Juillet 2026 | Correction 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.6 | Juillet 2026 | Correction 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.0 | Juillet 2026 | Nouvelle 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.0 | Juillet 2026 | Sé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.1 | Juillet 2026 | Correction 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.2 | Juillet 2026 | Nouveau 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.3 | Juillet 2026 | Affichage des colonnes de type date au format jj/mm/aaaa en lecture. |
| v3.9.4 | Juillet 2026 | Dé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.0 | Août 2026 | Passage à 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.1 | Août 2026 | Fusion 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.2 | Août 2026 | Deux 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.0 | Août 2026 | Cinq 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.0 | Août 2026 | Fusion 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.0 | Août 2026 | Ré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.0 | Août 2026 | Ré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>
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;
}
}
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_action | Description | CSRF requis |
|---|---|---|
| lire | Lecture des données (GET-like) | Non |
| sauvegarder | INSERT (row_id < 0) ou UPDATE (row_id > 0) | Oui |
| supprimer | DELETE par clé primaire | Oui |
| options_cascade | Rechargement options select dépendant | Non |
| options_recherche | Recherche AJAX pour colonne à fort volume (sql_options_ajax) v3.9.0 | Non |
| exporter | Retourne les données pour export XLSX | Non |
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
| Option | Type | Défaut | Description |
|---|---|---|---|
| id | string | auto | Identifiant unique de la grille (recommandé : le fixer manuellement) |
| db_host | string | localhost | Hôte MySQL |
| db_port | string | 3306 | Port MySQL |
| db_name requis | string | — | Nom de la base de données |
| db_user requis | string | — | Utilisateur MySQL |
| db_password requis | string | — | Mot de passe MySQL |
| db_charset | string | utf8mb4 | Charset connexion PDO |
| table requis | string | — | Nom de la table pour INSERT/UPDATE/DELETE |
| sql requis | string | — | Requête SELECT (peut être un SELECT complexe avec JOIN) |
| edit_mode | string | inline | inline | modal | cell |
| ajout_mode | string | =edit_mode | inline | modal — mode spécifique pour l'ajout (utile avec edit_mode='cell') |
| modal_titre | string | =id | Titre affiché dans la fenêtre modale |
| ajout | bool | true | Afficher le bouton d'ajout |
| label_ajouter | string | + Ajouter | Texte du bouton d'ajout |
| delete | bool | false | Activer la suppression (passe le rôle en 'admin') |
| read_only | bool | false | Mode consultation uniquement, aucune modification. Affiche un bandeau discret (icône 👁, style neutre) v3.8.0 |
| read_only_msg | string | auto | Message affiché en mode lecture seule. Défaut depuis v3.8.0 : « Consultation — ce tableau n'est pas modifiable avec votre rôle. » |
| show_action_buttons | bool | true | Afficher la colonne Actions |
| duplication | bool | false | Bouton ⧉ pour dupliquer une ligne |
| import_csv | bool | false | Bouton d'import CSV (3 étapes) |
| csv_separateur | string | ; | Séparateur CSV : ; ou , |
| recherche | bool | true | Champ de recherche globale |
| pagination | bool | true | Activer la pagination |
| longueur_defaut | int | 25 | Nombre de lignes par page par défaut |
| show_nbr_lines | bool | true | false = masque à la source le sélecteur "Afficher X lignes" (ni généré, ni sa règle CSS) v3.8.0 |
| ordre_defaut | array | [0,'asc'] | Colonne et sens de tri initial : [indexColonne, 'asc'|'desc'] |
| filtre_auto | bool | true | Ligne de filtres sous les en-têtes |
| totaux | bool | true | Ligne de totaux pour les colonnes number |
| symbole_monnaie | string | € | Symbole monétaire global |
| serverside | bool | false | Pagination/tri/filtrage côté serveur (recommandé > 5 000 lignes) |
| export | bool | false | Bouton export |
| export_filename | string | export | Nom du fichier exporté (sans extension) |
| export_formats | array | ['xlsx'] | Formats proposés : 'xlsx' et/ou 'csv'. Un seul format = clic direct (comportement inchangé) ; deux formats = menu déroulant v3.11.0 |
| action_custom | bool | false | Bouton supplémentaire dans la colonne Actions, tire acgr:action-custom — voir section Événements JS v3.11.0 |
| action_custom_icone | string | ⚡ | Glyphe du bouton d'action custom (comme ⧉ pour dupliquer) v3.11.0 |
| action_custom_label | string | null | Bulle d'aide du bouton d'action custom. null = libellé générique L('action_custom') v3.11.0 |
| epingle | bool | true | Afficher la colonne étoile d'épinglage (ignoré si read_only:true) |
| epingle_page_1 | bool | false | true = 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 |
| cookies | bool | true | Mémoriser les préférences (ordre, colonnes masquées/fixées, filtres, tris, recherche, pagination) en localStorage |
| debug | bool | false | Afficher les réponses non-JSON du serveur dans un panneau dédié |
| labels | array | [] | Surcharge des labels d'interface (voir section i18n) |
| preset | string|null | null | Raccourci pré-remplissant plusieurs options d'un coup (voir tableau ci-dessous). null = comportement identique à son absence v3.12.0 |
| defaut_actif | bool | false | false (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_suppression | callable | null | function(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.
| Valeur | Usage typique |
|---|---|
mini | Grille minimale lecture seule, clientside, sans recherche ni filtres |
recherche | mini + recherche, filtres, cookies |
edition | CRUD en modale, serverside |
edition_avancee | edition + édition cellule par cellule, ajout inline, épingles |
complet (défaut) | Comportement standard de la classe, aucune restriction |
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).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é | Type | Défaut | Description |
|---|---|---|---|
| champ requis | string | — | Nom du champ SQL retourné par la requête |
| titre | string | =champ | En-tête de colonne affiché |
| type | string | text | text | number | date | datetime | checkbox | select | textarea | email | url | tel | color email/url/tel/color v3.11.0 |
| editable | bool|string | true | false = 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 |
| pk | bool | false | true = 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 |
| formule | string | null | Colonne 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 |
| requis | bool | false | Champ obligatoire — validé côté PHP et JS |
| visible | bool | true | false = champ caché, utilisé pour les FK parentes |
| largeur | string | auto | Largeur CSS : '120px', '10%' |
| champ_table | string | =champ | Nom réel du champ en base si différent de l'alias SQL |
| monetaire | bool | false | Affichage formaté avec séparateur milliers et symbole |
| symbole | string | =symbole_monnaie | Symbole monétaire spécifique à cette colonne |
| total | bool | true | false = exclut cette colonne des lignes de totaux (utile pour un champ number sémantiquement non sommable, ex : une année) v3.7.1 |
| agregat | string | somme | somme | 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 |
| pattern | string | null | Regex (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 |
| dupliquer | bool | true | false = champ non copié lors d'une duplication de ligne |
| format_conditionnel | array | null | Coloration selon valeur (voir section dédiée) |
| url_image | bool | false | Afficher la valeur comme vignette image cliquable |
| image_taille | string | 40px | Taille de la vignette (carré) |
| image_round | string | none | round | rounded | square | none |
| widget | string | null | Nom 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_ancre | string | div | input (ac_telephone) | div (ac_carte) |
| widget_options | array | [] | Options passées au constructeur du widget |
| defaut | mixed | null | Valeur 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_libelle | string | null | Colonne à 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_ajax | string | null | Colonne à 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 |
| placeholder | string | null | Texte indicatif dans le champ vide (attribut placeholder natif). Édition inline, modal et cellule ; ignoré sur select et checkbox v3.13.0 |
| aide | string | null | Bulle 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 |
| min | number | null | Borne minimale native (attribut min), colonnes type:'number' uniquement v3.13.0 |
| max | number | null | Borne maximale native (attribut max), colonnes type:'number' uniquement v3.13.0 |
| step | number | null | Pas d'incrément natif (attribut step), colonnes type:'number' uniquement v3.13.0 |
| maxlength | int | null | Longueur maximale de saisie (attribut maxlength natif), colonnes type:'text' et type:'textarea' v3.13.0 |
| decimales | int | null | null (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 |
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.- 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
disabledaffichant la valeur (libellé résolu pour unselect, comme en lecture inline), sans attributdata-champ: il n'est donc jamais soumis au serveur. requissur 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.
'({prix_ht} * {tva}) * {nbr_articles}'. Opérateurs supportés : + - * / ( ).
- Non éditable directement —
editable:falseest forcé automatiquement dès qu'une colonne porteformule, pas besoin de le déclarer soi-même. - Recalcul en direct — un champ verrouillé (input
disabled, maisdata-champconservé 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
0par 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. Pourac_datagrid_proavec 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
formulene peut pas référencer une autre colonneformule. 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).
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.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']
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.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.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).
| Type | input.type | Pattern de repli (si non vide) |
|---|---|---|
^[^\s@]+@[^\s@]+\.[^\s@]+$ | ||
| url | url | ^https?://.+\..+ |
| tel | tel | ^[+]?[0-9 .\-()]{6,20}$ — générique, aucun pays présumé |
| color | color | aucun — 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}$']
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
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 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 ?
- > 5 000 lignes : recommandé
- > 50 000 lignes : indispensable
- Testé à 200 000 lignes avec temps de réponse < 1s
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).
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],
// ...
]));
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',
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
- Fichier — glisser-déposer ou sélection. Lecture via FileReader API (côté client).
- Mapping — association automatique par nom de colonne (insensible à la casse), ajustable manuellement.
- Prévisualisation — aperçu des 5 premières lignes. Import ligne par ligne avec barre de progression et rapport d'erreurs.
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
]]
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ègle | Description |
|---|---|
| operateur | > >= < <= = (ou ==) |
| valeur | Valeur de comparaison (nombre, ou date ISO si la colonne est type => 'date'/'datetime') |
| couleur | Couleur CSS appliquée si la règle correspond |
| gras | bool, 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']
<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
input— le widget s'attache à un<input hidden>(ac_telephone_multipays)div— le widget s'attache à un<div>conteneur (ac_carte, ac_note_etoiles…)
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
- Affichée uniquement si
table.scrollWidth > tableWrap.clientWidth— invisible si tout le tableau est visible. - Recalculée après chaque
render()(changement de page, filtre, tri…) viarequestAnimationFrame. - Recalculée lors du redimensionnement de la fenêtre (
window.resize). - Aucune option de configuration requise — la fonctionnalité est toujours active.
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 FR | Description |
|---|---|---|
| afficher | Afficher | Sélecteur nb lignes |
| lignes | lignes | Sélecteur nb lignes |
| rechercher | Rechercher : | Label du champ de recherche |
| aucun_resultat | Aucun résultat | Info pagination vide |
| aucune_donnee | Aucune donnée disponible | Tableau vide |
| enregistrer | Enregistrer | Bouton modal |
| annuler | Annuler | Bouton annuler |
| valider | Valider | Bouton valider (inline) |
| editer | Éditer | Bouton éditer |
| supprimer | Supprimer | Bouton supprimer |
| dupliquer | Dupliquer cette ligne | Tooltip bouton ⧉ |
| nouveau | ✦ Nouveau | Badge modal ajout |
| edition | ✎ Édition | Badge modal édition |
| champ_requis_js | Le champ «%s» est obligatoire. | Validation JS (%s = nom du champ) |
| champ_requis | Le champ «%s» est obligatoire. | Validation PHP côté serveur |
| insert_ok | Enregistrement ajouté. | Message PHP succès INSERT |
| update_ok | Modifications enregistrées. | Message PHP succès UPDATE |
| delete_ok | Enregistrement supprimé. | Message PHP succès DELETE |
| erreur_reseau | Impossible 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-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);
});
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);
});
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.
// e.detail.donnees : objet avec les valeurs insérées
// e.detail.grille_id : id de la grille
// e.detail.donnees : objet avec les valeurs mises à jour
// e.detail.grille_id : id de la grille
// e.detail.id : id supprimé
// e.detail.grille_id : id de la grille
// 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
// e.detail.message : message lisible
// e.detail.code : code machine (CHAMP_REQUIS, RUNTIME_ERROR…)
wrap.addEventListener('acgr:erreur', e => afficherErreur(e.detail.message));
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);
});
id < 0).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()
})
});
});
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.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);
}
});
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 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ètre | Type | Description |
|---|---|---|
| $donnees | array | [champ => valeur], tous les champs éditables + invisibles/FK |
| $estNouveau | bool | true pour un INSERT, false pour un UPDATE |
| $rowId | ?int | null si $estNouveau, sinon l'id de la ligne modifiée |
$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.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ètre | Type | Description |
|---|---|---|
| $rowId | int | Clé primaire de la ligne visée par la suppression |
| $ligne | array | [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) |
rep.message, événement acgr:erreur, code SUPPRESSION_BLOQUEE). Tout le reste (null, chaîne vide, absence de return) autorise la suppression.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.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.
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 document — toutes 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'));
});
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.
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 :
- Le Content-Type reçu
- Les 2000 premiers caractères du contenu brut
- Un log
console.errorcomplet
Accessibilité WCAG
ac_datagrid implémente les recommandations WCAG 2.1 niveau AA :
Sémantique ARIA
role="grid"sur la table avecaria-labelrole="row"sur tous les<tr>role="gridcell"sur tous les<td>role="columnheader"sur tous les<th>aria-sort="ascending|descending|none"sur les colonnes triablesaria-live="polite"sur la zone d'info paginationaria-labelsur tous les boutons icônes (traduit via i18n)aria-expandedsur les boutons de sous-grille
Navigation clavier (mode cell uniquement)
| Touche | Action |
|---|---|
| Tab / Shift+Tab | Navigation entre éléments interactifs |
| Enter / F2 | Entrer en mode édition sur la cellule focusée |
| Escape | Annuler 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éthode | Retour | Description |
|---|---|---|
| __construct(array $cfg) | — | Instanciation. Démarre la session et génère le token CSRF. |
| estRequeteAjax(): bool | bool | Retourne true si la requête POST correspond à cette grille. |
| traiterAjax(): void | void | Traite la requête AJAX et envoie la réponse JSON. Appeler exit; après. |
| genereHTML(): string | string | Retourne le HTML de la grille (conteneur + filtres externes). |
| genereCSS(string $theme): string | string | Retourne la balise <style> complète. Thèmes : sobre | techno | nature | pastel. |
| genereJS(): string | string | Retourne la balise <script> avec le moteur JS complet + config. |
| buildConfigJS(): array | array | Retourne la configuration sérialisable en JSON. Utile pour un rebuild dynamique. |
| getJSCore(): string | string | Retourne le code JS brut (sans balise script, sans config). Utile pour la mise en cache. |
| getPrimaryKey(): string | string | Retourne 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(): array | array | Retourne le tableau des instances sous-grille. |
| getVersion(): string | string | Statique. Retourne la version : '3.14.0'. |
| genererTokenCsrf(): string | string | Statique. Génère ou retourne le token CSRF de session (hex 64 chars). |
| lblStatic(array $labels, string $cle, ...$args): string | string | Statique. 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çaise | Termes anglais courants |
|---|---|
| preset | preset |
| edit_mode | editMode |
| ajout_mode | insertMode, addMode |
| modal_titre | modalTitle |
| ajout | add, insert, canAdd |
| label_ajouter | addButtonLabel |
| read_only | readOnly |
| read_only_msg | readOnlyMessage |
| show_action_buttons | showActions, actionsColumn |
| duplication | duplicate |
| import_csv | importCsv, csvImport |
| csv_separateur | csvSeparator, delimiter |
| recherche | search |
| longueur_defaut | perPage, pageSize, pageLength |
| show_nbr_lines | showPageSizeSelector, rowsPerPageSelector |
| ordre_defaut | defaultSort, initialSort |
| filtre_auto | autoFilter, columnFilters, filterRow |
| totaux | totals, showTotals |
| symbole_monnaie | currencySymbol |
| serverside | serverSide |
| export_filename | exportFilename |
| export_formats | exportFormats |
| action_custom | customAction |
| action_custom_icone | customActionIcon |
| action_custom_label | customActionLabel, customActionTooltip |
| epingle | pinned, pin |
| epingle_page_1 | pinnedOnFirstPage |
| cookies | localStorage, persistence, remember preferences |
| labels | labels, i18n, translations |
Au niveau colonne :
| Clé française | Termes anglais courants |
|---|---|
| champ | field |
| titre | title, label, header |
| requis | required |
| visible | visible, hidden |
| largeur | width |
| parent_label | parentLabel |
| widget_options | widgetOptions |
| widget_ancre | widgetAnchor, widgetContainer |
| monetaire | monetary, currency |
| symbole | symbol |
| dupliquer | duplicate (colonne) |
| format_conditionnel | conditionalFormat, conditionalFormatting |
| url_image | imageUrl |
| image_taille | imageSize |
| image_round | imageRound, roundedImage, avatar |
| agregat | aggregate, aggregation |
| pattern | pattern, regex, validation |
| options_display | optionsDisplay, displayOptions |
| champ_libelle | labelField, displayField |
| recherche_ajax | ajaxSearch, remoteSearch, searchAsYouType |
| pk | primaryKey, isPrimaryKey |
ac_datagrid v3.14.0 · artisan-code.fr · 18 août 2026