ac_datagrid_pro.js
Composant JavaScript pur — zéro dépendance — backend agnostique
1. Quick start
Une seule balise <script>, quelques lignes de configuration —
et votre grille est en ligne. Pas de npm, pas de bundler, pas de framework.
<script src="ac_datagrid_pro.js"></script>
Choisissez votre mode selon votre situation :
✦ Mode data — zéro backend
Passez un tableau JS directement. Aucun serveur, aucune API. Idéal pour prototyper ou afficher des données fixes.
⇄ Mode url — avec API
Pointez vers un endpoint REST. La grille gère pagination, tri et filtres côté serveur automatiquement.
Mode data — zéro backend
Le moyen le plus rapide de démarrer. Vous n'avez besoin d'aucun serveur, d'aucune API, d'aucune base de données. Passez votre tableau et la grille fait le reste — tri, filtres, pagination, tout est géré côté client.
<div id="ma-grille"></div>
<script src="ac_datagrid_pro.js"></script>
<script>
new AcDatagridPro({
target: '#ma-grille',
data: [
{ id: 1, nom: 'Dupont', prenom: 'Marie', ville: 'Bordeaux' },
{ id: 2, nom: 'Martin', prenom: 'Thomas', ville: 'Lyon' },
{ id: 3, nom: 'Petit', prenom: 'Claire', ville: 'Paris' },
],
colonnes: [
{ champ: 'id', titre: 'ID', editable: false },
{ champ: 'nom', titre: 'Nom' },
{ champ: 'prenom', titre: 'Prénom' },
{ champ: 'ville', titre: 'Ville' },
]
});
</script>
cfg.data est un tableau, cfg.url devient facultatif — la grille tourne
entièrement en mémoire (tri, filtres, pagination, CRUD simulé), sans jamais toucher fetch().
Sans preset déclaré, le preset mini s'applique : affichage simple, lecture seule, pagination.
Ajoutez preset: 'recherche' pour obtenir tri, recherche et filtres automatiques sur vos données.
Mode url — avec API
Pointez vers votre endpoint REST. La grille envoie des requêtes HTTP (GET par défaut pour la lecture, voir contrat d'API) et attend une réponse normalisée.
cfg.serverside
vaut toujours false par défaut, que vous passiez data ou url. Avec
url + serverside:false, la grille fonctionne quand même : un seul chargement
récupère toutes les lignes, puis tri/filtres/pagination se font côté client — correct sur un petit jeu de
données, mais à éviter au-delà de quelques milliers de lignes. Pour un vrai gros volume,
précisez serverside: true explicitement.
new AcDatagridPro({
target: '#ma-grille',
url: '/api/clients', // votre endpoint REST
preset: 'edition', // CRUD activé
serverside: true, // recommandé dès que le volume dépasse quelques milliers de lignes
colonnes: [
{ champ: 'id', titre: 'ID', editable: false },
{ champ: 'nom', titre: 'Nom', requis: true },
{ champ: 'email', titre: 'Email', type: 'email' },
]
});
Le contrat d'API attendu est décrit dans la section API référence.
2. Cas concrets
Tableau filtrable — zéro backend
Vous avez des données JSON (un export, une réponse d'API tierce, un fichier statique) et vous voulez les afficher avec tri, recherche et filtres. Aucun serveur nécessaire.
const produits = [
{ id: 1, ref: 'REF-001', nom: 'Câble HDMI 2m', categorie: 'Câbles', prix: 12.90, stock: 48 },
{ id: 2, ref: 'REF-002', nom: 'Souris sans fil', categorie: 'Périph.', prix: 29.90, stock: 12 },
{ id: 3, ref: 'REF-003', nom: 'Clavier mécanique', categorie: 'Périph.', prix: 89.00, stock: 5 },
{ id: 4, ref: 'REF-004', nom: 'Hub USB-C 7 ports', categorie: 'Câbles', prix: 45.00, stock: 0 },
];
new AcDatagridPro({
target: '#grille-produits',
data: produits,
preset: 'recherche', // tri + recherche globale + filtres automatiques
colonnes: [
{ champ: 'ref', titre: 'Réf.', editable: false, largeur: '100px' },
{ champ: 'nom', titre: 'Produit' },
{ champ: 'categorie', titre: 'Catégorie', type: 'select',
options: [{ id: 'Câbles', libelle: 'Câbles' }, { id: 'Périph.', libelle: 'Périphériques' }] },
{ champ: 'prix', titre: 'Prix', type: 'number', monetaire: true },
{ champ: 'stock', titre: 'Stock', type: 'number',
format_conditionnel: [{ operateur: '=', valeur: 0, couleur: '#dc2626', gras: true }] },
]
});
Le preset recherche gère tri, recherche et filtres entièrement en mémoire,
sans aucune requête serveur. La colonne stock passe en rouge gras quand elle vaut 0.
Grille éditable — CRUD avec API
Votre backend expose un endpoint REST. La grille gère l'interface complète : ajout, modification, suppression avec confirmation — sans une ligne de JS supplémentaire.
new AcDatagridPro({
target: '#grille-contacts',
url: '/api/contacts',
preset: 'edition',
serverside: true,
colonnes: [
{ champ: 'id', titre: 'ID', editable: false },
{ champ: 'nom', titre: 'Nom', requis: true },
{ champ: 'prenom', titre: 'Prénom', requis: true },
{ champ: 'email', titre: 'Email', type: 'email' },
{ champ: 'telephone', titre: 'Tél.', type: 'tel' },
{ champ: 'id_statut', titre: 'Statut', type: 'select',
options: [
{ id: 1, libelle: 'Actif' },
{ id: 2, libelle: 'Inactif' },
{ id: 3, libelle: 'Prospect'},
]
},
]
});
Le preset edition inclut : tri, recherche, filtres, CRUD en modale
(création / modification / suppression), et persistance de l'état en localStorage.
action. La grille appelle des routes distinctes selon l'action
(GET pour lire, POST/PUT pour écrire, DELETE
pour supprimer) — voir la section API référence pour le détail exact,
et cfg.routes pour adapter la grille à un backend qui suit d'autres conventions
(endpoint unique compris, si c'est votre cas).
Grille complète — export, épingles, totaux
Pour les cas métier avancés : le preset complet active toutes les fonctionnalités.
new AcDatagridPro({
target: '#grille-commandes',
url: '/api/commandes',
preset: 'complet',
serverside: true,
export: true,
export_formats: ['csv', 'xlsx'],
export_filename: 'commandes',
theme: 'techno',
colonnes: [
{ champ: 'id', titre: 'N°', editable: false, largeur: '70px' },
{ champ: 'client', titre: 'Client', requis: true },
{ champ: 'date', titre: 'Date', type: 'date' },
{ champ: 'montant_ht', titre: 'HT', type: 'number', monetaire: true,
agregat: 'somme',
format_conditionnel: [{ operateur: '>=', valeur: 10000, couleur: '#16a34a', gras: true }] },
{ champ: 'montant_ttc', titre: 'TTC', type: 'number', monetaire: true, agregat: 'somme' },
{ champ: 'statut', titre: 'Statut', type: 'select',
options: [
{ id: 'brouillon', libelle: 'Brouillon' },
{ id: 'envoyee', libelle: 'Envoyée' },
{ id: 'payee', libelle: 'Payée' },
{ id: 'annulee', libelle: 'Annulée' },
]
},
]
});
Le preset complet inclut tout : CRUD modale + cellule (double-clic), ajout inline,
épingles ★, duplication de ligne, export CSV/XLSX, import CSV, totaux et formatage conditionnel.
Les totaux ne sont plus déclarés dans une liste globale totals : chaque colonne porte
son propre agregat (somme par défaut ; voir Colonnes).
3. Options avancées
Presets
Les presets sont des configurations pré-packagées. Choisissez le plus proche de votre besoin et surchargez uniquement ce qui diffère.
Chaîne de fusion : Défauts → Preset → Config du développeur. La config explicite l'emporte toujours.
Lecture seule, clientside, pagination uniquement. Sans tri, sans recherche, sans filtres.
mini + recherche globale, filtres automatiques, persistance en localStorage. Lecture seule.
recherche + CRUD en modale (création/modification/suppression), serverside activé.
edition + édition cellule par cellule (double-clic), ajout inline, lignes épinglées.
Comportement standard de la classe, aucune restriction — totaux, duplication de lignes, export CSV/XLSX, import CSV. Tableau vide dans le code source : garantit qu'il ne diverge jamais des défauts de la classe.
data
Ces deux presets conviennent bien aux données statiques (zéro backend).
Avec url et un gros volume, préférez edition ou supérieur, et pensez à
ajouter serverside: true explicitement — voir la mise en garde de la section précédente.
Options générales
| Option | Type | Défaut | Description |
|---|---|---|---|
| target | string | requis* | Sélecteur CSS du conteneur (#ma-grille). *Ou un élément DOM directement ; facultatif si passé comme 1er argument du constructeur. |
| data | array | null | Données statiques directes. url devient facultatif si présent. |
| url | string | — | URL de l'endpoint API. data devient facultatif si présent. |
| colonnes | array | requis | Définition des colonnes (voir section Colonnes) |
| preset | string | 'mini' | mini · recherche · edition · edition_avancee · complet |
| serverside | bool | false | Jamais automatique, quel que soit data ou url — voir la mise en garde du Quick Start |
| pagination | bool | true | Activer la pagination |
| longueur_defaut | int | 25 | Lignes par page |
| edit_mode | string | 'inline' | inline · modal · cell |
| ajout_mode | string | 'inline' | inline · modal |
| theme | string | 'sobre' | sobre · techno · nature · pastel |
| ajout | bool | true | Afficher le bouton d'ajout |
| delete | bool | false | Activer la suppression |
| read_only | bool | false | Raccourci pour désactiver tout le CRUD |
| recherche | bool | true | Barre de recherche globale |
| filtre_auto | bool | true | Ligne de filtres automatiques par colonne |
| cookies | bool | true | Persistance état (tri, filtres, colonnes masquées, page) en localStorage |
| epingle | bool | true | Colonne étoile — épingler des lignes en tête |
| epingle_page_1 | bool | false | N'envoie les identifiants épinglés au serveur qu'en page 1 (optimisation réseau) |
| duplication | bool | false | Bouton de duplication de ligne (⧉) |
| export | bool | false | Active le bouton export — formats dans export_formats |
| export_formats | array | ['xlsx'] | ['xlsx'], ['csv'] ou les deux (menu déroulant si 2 formats) |
| export_filename | string | 'export' | Nom du fichier exporté, sans extension |
| import_csv | bool | false | Import CSV en 3 étapes avec mapping visuel |
| totaux | bool | true | Ligne de totaux en pied de tableau (agrégat par colonne, voir Colonnes) |
| filtres | array | [] | Filtres externes (selects au-dessus de la grille) — voir Filtres externes |
| defaut_actif | bool | false | false = comportement d'origine inchangé, la clé defaut d'un filtre externe reste posée visuellement (selected) mais ignorée dans les requêtes au premier chargement. true = le defaut s'applique réellement dès le premier chargement, persiste en localStorage, et resynchronise visuellement le select. v3.13.0 |
| modal_titre | string | null | Titre de la modale d'édition |
| sous_grilles | array | [] | Tableau de définitions de sous-grilles (pas un objet unique) — voir Sous-grilles |
| routes | object | {} | Surcharge la construction/interprétation d'une route REST, action par action — voir API référence |
| enTetes | function | null | (action, params) => ({...}) — en-têtes HTTP additionnels (authentification) |
| labels | object | {} | Surcharger les libellés i18n |
| debug | bool | false | Logs détaillés en console |
crud (objet {insert,update,delete}), sort, resize
et formatConditionnel (interrupteur global) n'existent plus. Le tri et le
redimensionnement des colonnes sont désormais toujours actifs, sans interrupteur ; le CRUD
granulaire se pilote via ajout / delete / edit_mode ; le
formatage conditionnel s'active simplement en le déclarant sur une colonne, sans clé globale.
Colonnes
Chaque colonne est un objet dans le tableau colonnes :
| Propriété | Type | Défaut | Description |
|---|---|---|---|
| champ | string | requis | Nom du champ dans les données JSON |
| titre | string | = champ | Titre affiché dans l'en-tête |
| type | string | 'text' | text · select · date · datetime · number · email · url · tel · color · textarea · checkbox |
| editable | bool|string | true | false = colonne en lecture seule (ex: clé primaire, par heuristique). 'creation' = saisissable uniquement à la création, verrouillé et visible en modification — voir la note ci-dessous 'creation' v3.13.0 |
| pk | bool | false | true = désigne explicitement la clé primaire, prioritaire sur l'heuristique editable:false pour getPK() (détection de ligne existante vs nouvelle, route PUT vs POST). Exclue d'office du corps envoyé au backend REST, même si elle n'est pas editable:false — même protection que côté ac_datagrid.php v3.13.0 |
| 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 (même normalisation que ac_datagrid.php). Recalculée en direct côté navigateur à la saisie des champs sources — voir la note ci-dessous v3.14.0 |
| visible | bool | true | false = champ transmis aux requêtes mais non affiché |
| requis | bool | false | Validation côté grille avant envoi |
| largeur | string | auto | Largeur CSS ('60px', '15%') |
| options | array | — | Options statiques [{id, libelle}] pour type select |
| monetaire | bool | false | Affichage monétaire avec séparateurs et symbole |
| agregat | string | 'somme' | somme · moyenne · min · max · nombre — fonction du total en pied de tableau |
| total | bool | true | Inclure cette colonne dans la ligne de totaux |
| format_conditionnel | array/object | — | Formatage conditionnel (voir ci-dessous) |
| defaut | mixed | '' | Valeur par défaut à la création d'une nouvelle ligne v3.13.0 |
| placeholder | string | null | Texte indicatif dans le champ vide (attribut placeholder natif), tous types de saisie sauf select et checkbox v3.13.0 |
| aide | string | null | Bulle d'aide (attribut title), sur le champ en édition ET sur l'en-tête de colonne v3.13.0 |
| min | number | null | Borne minimale native (attribut min), colonnes type:'number' v3.13.0 |
| max | number | null | Borne maximale native (attribut max), colonnes type:'number' v3.13.0 |
| step | number | null | Pas d'incrément natif (attribut step), colonnes type:'number' v3.13.0 |
| maxlength | int | null | Longueur maximale (attribut maxlength natif), colonnes type:'text' et type:'textarea' v3.13.0 |
| decimales | int | null | null = comportement d'origine inchangé. Posé : plafond de décimales à l'affichage sans zéro forcé (5 reste 5, 5,25 reste 5,25 avec decimales:2). S'applique à toute colonne type:'number', avec ou sans monetaire:true v3.13.0 |
editable:true (inline, modale, duplication). Sur une ligne existante : non éditable en inline ; en modale, le champ reste visible mais verrouillé — un input disabled affichant la valeur (libellé résolu pour un select), sans attribut data-champ, donc jamais soumis à l'API. requis sur un tel champ n'est validé qu'à la création.
'({prix_ht} * {tva}) * {nbr_articles}'. Opérateurs supportés : + - * / ( ).
- Non éditable directement —
editable:falseest forcé automatiquement dès qu'une colonne porteformule, même normalisation que côtéac_datagrid.php. - 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 inline, en modale, et immédiatement à l'ouverture d'une ligne dupliquée. - Champ source manquant ou non numérique → la colonne calculée reste vide, jamais
0par défaut. - Backend externe et calcul serveur — contrairement à
ac_datagrid.php(qui recalcule systématiquement côté PHP, sans jamais faire confiance au client),ac_datagrid_pron'a pas de code serveur propre : c'est la valeur calculée côté navigateur qui est envoyée telle quelle dans le corps de la requêtesauvegarder(POST/PUT), au même titre que les autres champs. Un développeur qui souhaite une vérification serveur (recommandé si la donnée a une valeur métier sensible, ex. facturation) doit l'implémenter dans son propre backend REST — la classe ne le fait pas à sa place. - Sécurité côté navigateur — après substitution des
{champ}par leur valeur numérique, l'expression est validée par une whitelist stricte (chiffres, points, opérateurs arithmétiques uniquement) avant toute évaluation : aucune exécution de code arbitraire possible, même siformuleprovenait d'une source non fiable. - 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.
Formatage conditionnel
{ champ: 'salaire', titre: 'Salaire', type: 'number',
format_conditionnel: [
{ operateur: '<', valeur: 30000, couleur: '#dc2626' }, // rouge
{ operateur: '>', valeur: 50000, couleur: '#16a34a', gras: true }, // vert gras
]
}
Opérateurs : = == < > <= >=
— Propriétés de règle : couleur · gras (bool, optionnel).
Les règles sont évaluées dans l'ordre, la première qui correspond gagne.
Forme alternative pour un seuil unique (deux/trois couleurs autour d'une valeur de référence) :
{ seuil, superieur, egal, inferieur } — voir la documentation ac_datagrid
pour le détail, le mécanisme est strictement identique des deux côtés.
background et icon ne sont plus supportés
Une version antérieure (non finalisée, marquée TODO dans son propre code) esquissait des propriétés
background et icon par règle, avec seulement 3 opérateurs (=,
>, <). La version actuelle couvre 6 opérateurs mais seulement
couleur/gras. Ces deux propriétés pourraient revenir dans une prochaine
version si le besoin se confirme.
Filtres externes v3.13.0
Des selects (ou champs date) de filtrage peuvent être placés au-dessus de la grille, en plus du
filtre_auto par colonne. Contrairement à ac_datagrid (PHP), il n'y a pas
de sql_options — chaque filtre fournit directement son tableau d'options en JS, le
transport REST n'ayant pas de notion de requête SQL à exécuter côté classe.
new AcDatagridPro('#ma-grille', {
url: '/api/commandes',
filtres: [
{
id: 'filtre_statut',
label: 'Statut',
champ_sql: 'statut', // clé transmise au backend
options: [
{ id: 0, libelle: 'En cours' },
{ id: 1, libelle: 'Terminée' },
],
defaut: 0, // v3.13.0 — nécessite defaut_actif ci-dessous
},
],
defaut_actif: true, // v3.13.0 — sans ça, 'defaut' reste cosmétique
colonnes: [ /* ... */ ],
});
defaut d'un filtre pose bien la bonne
option comme selected à la construction du select (voir facade.js), mais
seule defaut_actif:true (au niveau grille) fait réellement passer cette valeur dans les
requêtes dès le premier chargement, et la fait persister en localStorage d'un
rechargement de page à l'autre. Sans defaut_actif:true, le select affiche la bonne
valeur mais la grille charge sans filtre au premier chargement — un décalage purement visuel,
resté silencieux jusqu'à cette version.
| Propriété | Type | Description |
|---|---|---|
| id | string | Identifiant du filtre, utilisé en interne (clé de filtresActifs) |
| label | string | Libellé affiché au-dessus du champ |
| champ_sql | string | Nom du paramètre transmis au backend dans la requête |
| type | string | select (défaut) · date_debut · date_fin |
| options | array | [{id, libelle}] — ignoré pour les types date |
| defaut | mixed | Valeur présélectionnée. Nécessite defaut_actif:true au niveau grille pour avoir un effet réel sur les requêtes v3.13.0 |
Thèmes
4 thèmes CSS embarqués dans la classe — aucun fichier externe, activables via theme :
| Valeur | Description |
|---|---|
| sobre | Bleu navy, sobre et professionnel — idéal pour les back-offices |
| techno | Mode sombre, accents cyan — pour les interfaces techniques |
| nature | Tons verts et terreux — pour les applications grand public |
| pastel | Tons doux et rosés — pour les interfaces créatives et RH |
CRUD — Modes d'édition
edit_mode: 'inline' (défaut)
La ligne entière se transforme en formulaire. Boutons ✔ Valider et ✕ Annuler à droite.
edit_mode: 'modal'
Un formulaire s'ouvre dans une modale. Tous les champs sur une seule vue. Idéal pour les formulaires complexes.
edit_mode: 'cell'
Double-clic sur une cellule pour l'éditer directement. Entrée = valider, Échap = annuler. Idéal pour les corrections rapides.
Granularité CRUD
// Désactiver uniquement la suppression
ajout: true, delete: false
// Lecture seule (raccourci)
read_only: true
acgr:apres-insert pour une navigation personnalisée.
Widgets externes v1.1.0
Une colonne peut remplacer son champ de saisie par défaut par un composant JavaScript riche que vous avez écrit vous-même — un sélecteur de carte, un champ téléphone international, un éditeur enrichi... Le composant n'a besoin d'aucune dépendance particulière ; il respecte simplement un contrat minimal, comme une prise électrique.
Le contrat côté widget
Votre classe JavaScript doit exposer :
- un constructeur
new MonWidget(ancreId, options) - une méthode
setValeur(valeur, libelleInitial)— appelée pour préremplir la valeur existante à l'ouverture (libelleInitialvient decol.champ_libellesi déclaré, sinonnull) - une méthode
getValeur()— appelée pour récupérer la valeur saisie à la sauvegarde - optionnellement, une propriété statique
MODES_SUPPORTES(ex.['modal','inline']) — sans elle, le widget n'est utilisé qu'enedit_mode:'modal'
Déclaration dans la colonne
{
champ: 'adresse',
titre: 'Adresse',
widget: 'MonWidgetCarte', // nom de la classe, exposée sur window
widget_ancre: 'div', // 'div' (widget visuel) ou 'input' (widget qui gère une valeur simple)
widget_options: { zoom: 14 } // transmis tel quel au constructeur, complété automatiquement
}
widget n'existe pas sur window, ou si son
instanciation échoue, la cellule retombe automatiquement sur un champ texte classique — la grille
ne casse jamais, même si le widget n'a pas été chargé sur la page.
widget_options reçoit automatiquement _acgr_url (= cfg.url),
_acgr_id et _champ — utile pour un widget qui a besoin de savoir sur
quelle grille/quel champ il travaille. Attention cependant : certains widgets de la Classothèque
(comme ac_big_select) font leur propre appel réseau, avec leur propre
format (FormData, action options_recherche) — indépendamment de
cfg.routes. Contre un backend REST externe, ce widget précis n'enverra pas une requête
compatible avec votre API tant qu'il n'aura pas été adapté ou que votre backend n'implémente pas
cette route spécifique séparément. Sujet en cours de normalisation.
Lignes épinglées ★
Activez la colonne étoile avec epingle: true (activé par défaut). L'utilisateur épingle
des lignes en cliquant sur l'étoile — elles remontent en tête et restent visibles sur toutes les pages.
epinglees
(sauf si epingle_page_1: true, qui limite l'envoi à la page 1 par optimisation réseau).
new AcDatagridPro({
target: '#grille',
url: '/api/clients',
preset: 'edition_avancee', // épingles activées par edition_avancee et complet
colonnes: [ ... ]
});
Export CSV / XLSX
Activez l'export avec export: true et choisissez le(s) format(s) avec export_formats.
new AcDatagridPro({
url: '/api/data',
export: true,
export_formats: ['csv', 'xlsx'],
export_filename: 'mes_clients',
colonnes: [ ... ]
});
Sous-grilles (maître/détail)
acgr:row-click + setFiltreParent(). Ce n'est plus nécessaire : les
sous-grilles se déclarent directement dans la config du parent, sous sous_grilles
(un tableau, pas un objet unique) — la grille les enregistre et les initialise elle-même à
l'ouverture d'une ligne.
new AcDatagridPro({
target: '#praticiens',
url: '/api/praticiens',
colonnes: [ ... ],
sous_grilles: [{
subgrid_param: 'id_praticien', // champ transmis au backend de la sous-grille
url: '/api/patients',
colonnes: [
{ champ: 'id', titre: 'ID', editable: false },
{ champ: 'nom', titre: 'Nom' },
{ champ: 'tel', titre: 'Tél.' },
],
}],
});
Chaque objet de sous_grilles accepte les mêmes options qu'une grille normale
(routes, theme, serverside, preset...).
Plusieurs sous-grilles sont possibles (tableau, pas de limite à une seule).
Événements DOM
La grille émet des événements DOM personnalisés, qui remontent (bubbles) sur l'élément cible :
document.querySelector('#ma-grille')
.addEventListener('acgr:apres-insert', function(e) {
console.log('Champs insérés :', e.detail.donnees);
});
detail et un
événement (acgr:avant-suppression) qui n'existaient pas dans le code de l'époque —
la confirmation de suppression passe toujours par AcNotifModale.confirmer() (ou
confirm() natif en repli), appelé en interne, jamais par un événement DOM annulable.
Cela reste vrai : aucun événement n'intercepte la boîte de confirmation elle-même.
Les formes ci-dessous sont vérifiées contre le code source.
acgr:before_delete
(voir plus bas) se déclenche après que l'utilisateur a confirmé la suppression dans la boîte de dialogue,
mais avant que la requête réseau ne parte. Il permet un blocage programmatique par règle métier
(ex. ligne déjà facturée) — un cas d'usage différent de la confirmation UI ci-dessus, pas une résurrection
de l'ancien acgr:avant-suppression jamais implémenté.
acgr:apres-chargement v3.12.0
Émis après chaque chargement réussi (initial, pagination, tri, recherche, filtre)
{ total, page, grille_id }acgr:row-click
Clic sur une ligne (hors boutons/inputs)
{ id, grille_id, selected, row } — row (objet complet de la ligne) ajouté en v3.12.0acgr:apres-insert
Ligne insérée avec succès
{ donnees, grille_id } — donnees = champs envoyés au serveur (pas de row/id distincts)acgr:apres-update
Ligne mise à jour avec succès
{ donnees, grille_id }acgr:apres-suppression
Ligne supprimée avec succès
{ id, grille_id }acgr:apres-suppression-multiple
Suppression groupée des lignes épinglées terminée (bouton ⛔ de la barre d'outils)
{ ids, nb_ok, erreurs, grille_id } — ids = lignes visées, erreurs = tableau { id, message } pour celles en échecacgr:charger-sous-grille
Événement interne — déclenché à l'ouverture d'une ligne pour initialiser sa sous-grille. À usage interne : la déclaration via sous_grilles (voir section dédiée) s'en charge, pas besoin de l'écouter directement.
{ masterId, container, parentId }acgr:log
Émis sur document (pas sur le conteneur de la grille) après chaque CREATE/UPDATE/DELETE réussi — pratique pour brancher sa propre traçabilité côté page hôte
{ action, id, table, grille, donnees } — action = 'CREATE'|'UPDATE'|'DELETE'. donnees ajouté en v3.13.0 : champs de la ligne concernée.acgr:before_delete v3.13.0
Émis sur le conteneur de la grille, avant tout appel réseau, sur suppression simple ET groupée. Annulable via event.preventDefault() — bloque la suppression sans aller-retour serveur. Distinct de la confirmation AcNotifModale.confirmer() (voir l'encart ci-dessus) : ce nouvel événement se déclenche après la confirmation utilisateur mais avant la requête réseau, pour un blocage programmatique (règle métier), pas une confirmation UI.
{ id, ligne, table, grille_id } — ligne = objet complet de la ligne visée (ou null si introuvable localement)acgr:erreur
Erreur retournée par le backend ou réseau
{ message, code }Accessibilité v1.1.0
La grille pose automatiquement les attributs ARIA nécessaires pour qu'un lecteur d'écran ou une navigation 100% clavier comprenne sa structure et ses états — aucune configuration requise.
- Structure :
role="grid"sur la table,rowsur les lignes,columnheadersur les en-têtes,gridcellsur les cellules - Tri :
aria-sortreflète l'état réel (ascending/descending/none) sur chaque colonne triable - Épingles :
aria-labelexplicite ("Épingler" / "Désépingler") sur la cellule étoile — pas d'aria-pressed(contrairement à une version antérieure de cette doc) - Sous-grilles :
aria-expandedsur le bouton d'expansion, synchronisé à chaque ouverture/fermeture - Boutons d'action :
aria-labelexplicite sur éditer/dupliquer/supprimer (une icône seule n'est pas lisible par un lecteur d'écran) - Résultats :
aria-live="polite"sur la zone d'info — le nombre de résultats est annoncé automatiquement après un filtre ou un changement de page - Pagination :
aria-label="Pagination"sur le conteneur — pas derole="navigation"ni d'aria-currentpar page actuellement (contrairement à une version antérieure de cette doc)
Les libellés sont personnalisables via cfg.labels, comme tous les autres textes de la grille.
Méthodes publiques
load(), setData() et
getSelection() — seule la première existe, sous un autre nom
(charger()). setData() et getSelection() n'ont jamais été
construites dans la reconstruction actuelle. À garder en réserve pour une prochaine version.
instance.charger()
Recharge les données depuis le backend (ou re-filtre en mode data).
instance.setFiltreParent(champ, valeur)
Définit un filtre parent et recharge. Utilisé en interne pour les sous-grilles ; disponible
aussi en usage manuel si besoin d'un contrôle plus fin que la déclaration via sous_grilles.
instance.destroy()
Détruit l'instance, annule une requête réseau en vol s'il y en a une, vide le conteneur et libère les listeners.
AcDatagridPro.getVersion()
Méthode statique. Retourne la version du composant ("3.14.0").
Scrollbar horizontale miroir v1.0.5
Lorsque la grille déborde horizontalement, une scrollbar miroir apparaît au-dessus du tableau, synchronisée bidirectionnellement avec la scrollbar native du bas. Elle permet de faire défiler la grille sans avoir à atteindre le bas de la page.
Comportement automatique
- Affichée uniquement si
table.scrollWidth > tableWrap.clientWidth— invisible si tout tient à l'écran. - 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 — toujours active.
Cas des onglets cachés
Quand la grille est dans un onglet display:none au chargement, son clientWidth
est 0 et le calcul reste à zéro. Utiliser les hooks publics pour déclencher
un recalcul une fois l'onglet visible.
Hooks publics JS v1.0.5
Chaque instance expose trois mécanismes pour forcer le recalcul de la scrollbar miroir depuis l'extérieur. Tous sont opt-in — les grilles existantes fonctionnent sans modification.
1. Méthode directe sur le conteneur
document.getElementById('mon_id').acgrRefreshScroll();
2. Événement DOM ciblé
const container = document.getElementById('mon_id');
container.dispatchEvent(new CustomEvent('acgr:refresh-scroll'));
3. Événement global — toutes les grilles
Dispatché sur document — toutes les grilles de la page recalculent simultanément.
Idéal pour les systèmes d'onglets : un seul appel suffit.
// Dans le gestionnaire d'activation d'onglet :
document.getElementById('tab-' + id).classList.add('active');
// requestAnimationFrame garantit que display:block est effectif avant le calcul
requestAnimationFrame(function() {
document.dispatchEvent(new CustomEvent('acgr:refresh-scroll-all'));
});
requestAnimationFrame avant le dispatch —
sans lui, clientWidth peut encore être 0 si le browser n'a pas encore appliqué
le changement de classe CSS.
4. API référence
action dans un POST JSON. Ce n'est plus le contrat par défaut : la grille appelle
désormais 5 routes distinctes, avec de vraies méthodes HTTP. Si votre backend suit
encore l'ancien schéma à endpoint unique (ou tout autre schéma), cfg.routes permet de le
brancher tel quel — voir Adapter à un backend existant en fin de section.
Aucune obligation de migrer un backend qui fonctionne déjà.
La grille délègue tout au réseau via cfg.routes, avec ces valeurs par défaut :
| Action | Route par défaut | Détail |
|---|---|---|
| lire | GET {url}?... | Liste, pagination, recherche, filtres |
| sauvegarder | POST {url} (création) ou PUT {url}/{id} (modification) | Corps JSON = champs de la ligne |
| supprimer | DELETE {url}/{id} | — |
| options_cascade | GET {url}/cascade/{champ}?valeur_parent=... | Options d'un select dépendant |
| exporter | GET {url}/export?... | Mêmes filtres que lire |
Query params de lire — précisions importantes
GET {url}?acgr_id=...&start=0&length=25&search=...&order_tris=...&filtres_colonnes=...
Trois paramètres sont des chaînes JSON, pas des tableaux natifs :
| Paramètre | Contenu décodé |
|---|---|
| order_tris | [{"col":0,"sens":"asc"}] — index de colonne + sens |
| filtres_colonnes | {"nom":"dup"} — filtre par colonne, clé = nom du champ |
| epinglees | ["12","45"] — ids des lignes épinglées (absent si aucune) |
Réponse attendue
{
"data": [ { "id": 1, "nom": "Dupont", "prenom": "Marie" }, ... ],
"recordsFiltered": 1234,
"epinglees": [ { "id": 5, "nom": "Martin", ... }, ... ],
"totaux_generaux": { "montant": 128450 }
}
| Champ | Requis | Description |
|---|---|---|
| data | oui | Page de données courante (sans les épinglées) |
| recordsFiltered | non | Nombre total de lignes filtrées ; sans lui, la grille utilise data.length |
| epinglees | non | Données complètes des lignes épinglées PRO |
| totaux_generaux | non | Un agrégat déjà calculé par champ — voir l'avertissement ci-dessous |
totaux_generaux : le calcul est intégralement à votre charge
Contrairement à ac_datagrid.php — où la classe construit elle-même le SQL et traduit
agregat:'moyenne' en AVG() — ac_datagrid_pro.js ne calcule
jamais rien côté serveur, par nature (backend agnostique). La grille affiche tel quel
totaux_generaux[champ], avec le préfixe correspondant à col.agregat. Si
vous déclarez agregat:'moyenne' sur une colonne mais que votre backend renvoie une
somme SQL brute, la grille affichera cette somme avec un ⌀ devant, sans aucune erreur —
elle n'a pas les données brutes sous les yeux pour vérifier la cohérence. Faites correspondre le
calcul SQL/ORM à l'agregat déclaré sur chaque colonne.
Contrat d'API — sauvegarder
// Création — POST {url}
// Corps : { "nom": "Martin", "prenom": "Jean" }
// Réponse : { "id": 157 }
// Modification — PUT {url}/157
// Corps : { "nom": "Martin", "prenom": "Jean-Noël" }
// Réponse : {} (corps vide accepté, le code HTTP fait foi)
L'id retourné à la création est indispensable : c'est lui qui remplace le
row_id négatif temporaire de la ligne fraîchement créée côté client.
Contrat d'API — supprimer
// DELETE {url}/157
// Réponse : {} (ou 204 No Content)
Le corps n'est pas inspecté, seul le code HTTP compte.
Contrat d'API — options_cascade
// GET {url}/cascade/id_statut?valeur_parent=3
// Réponse : { "options": [ { "id": 1, "libelle": "Actif" }, { "id": 2, "libelle": "Inactif" } ] }
Gestion des erreurs
Le code HTTP fait foi : tout code < 400 est un succès, tout code ≥ 400 un échec.
{ "message": "Explication lisible", "code": "VALIDATION_ERREUR" }
message est affiché à l'utilisateur ; code est transmis dans l'événement acgr:erreur (e.detail.code, absent si non fourni).
Model::paginate($length) ne renvoie PAS la forme attendue — il renvoie
{data, current_page, total, per_page, ...}. Reconstruisez la réponse à la main :
{'data' => $page->items(), 'recordsFiltered' => $page->total()}.
$request->validate([...]) renvoie nativement 422 {"message":"...", "errors":{...}}
— la grille ne lit que .message (générique), .errors est ignoré. Attrapez
l'exception et aplatissez le message vous-même si vous voulez remonter le détail par champ.
Adapter à un backend existant
Chaque entrée de cfg.routes accepte construire et/ou mapper,
indépendamment — utile pour brancher la grille sur un backend qui suit d'autres conventions,
y compris l'ancien schéma à endpoint unique décrit plus haut :
new AcDatagridPro({
url: '/api/donnees.php',
routes: {
lire: {
construire(params) {
return { method: 'POST', url: '/api/donnees.php',
body: { action: 'load', page: Math.floor(params.start / params.length) + 1,
per_page: params.length, search: params.search } };
},
mapper(json) {
return { ok: json.status === 'ok', data: json.data,
recordsFiltered: json.total, totaux_generaux: json.totals };
},
},
// ... sauvegarder / supprimer / options_cascade / exporter, même principe
},
colonnes: [ ... ]
});
Pas d'authentification/CSRF intégrée pour ce protocole (stateless) : branchez la vôtre via
cfg.enTetes(action, params), appelé à chaque requête, qui retourne les en-têtes HTTP à fusionner
(typiquement Authorization: Bearer ...).
Feuille de route
ac_datagrid_pro est compilée depuis les mêmes sources que
ac_datagrid.php via un script de build commun — les deux classes portent désormais le
même numéro de version. Le tableau ci-dessous reste l'historique réel de la ligne 1.x ; la ligne
3.12.0 marque la bascule architecturale, pas une régression de version.
| Feature | Statut | Version |
|---|---|---|
| Constructeur + presets | ✅ Livré | 1.0.0.e-alpha |
| Tri multi-colonnes (Shift+clic) | ✅ Livré | 1.0.0.e-alpha |
| Pagination serverside/clientside | ✅ Livré | 1.0.0.e-alpha |
| Recherche globale + filtres auto-filter | ✅ Livré | 1.0.0.e-alpha |
| Cookies (persistance état) | ✅ Livré | 1.0.0.e-alpha |
| CRUD modal + cell | ✅ Livré | 1.0.0.e-alpha |
| CRUD inline | ✅ Livré | 1.0.0.e-alpha |
| Resize colonnes | ✅ Livré | 1.0.0.e-alpha |
| Épingles ★ (persistantes toutes pages) | ✅ Livré | 1.0.0.e-alpha |
| Totaux + formatage conditionnel | ✅ Livré | 1.0.0.e-alpha |
| 4 thèmes CSS embarqués | ✅ Livré | 1.0.0.e-alpha |
| Mode statique data: [] | ✅ Livré | 1.0.0.e-alpha |
| Export CSV / XLSX | ✅ Livré | 1.0.0.e-alpha |
| Import CSV (mapping visuel 3 étapes) | ✅ Livré | 1.0.0-beta |
| Menu contextuel colonnes (⋮) | ✅ Livré | 1.0.0.g-alpha |
| Sous-grilles imbriquées | ✅ Livré | 1.0.0.g-alpha |
| Colonnes fixées gauche/droite (⋮) | ✅ Livré | 1.0.0.h-alpha |
| Colonne expand ▶ sticky | ✅ Livré | 1.0.0.h-alpha |
| Duplication de ligne (⧉) | ✅ Livré | 1.0.0-beta |
| Debounce recherche globale (400ms) | ✅ Livré | 1.0.0.i-alpha |
| Sous-grille hors table maître (scroll indépendant) | ✅ Livré | 1.0.0-beta |
| Cookies sous-grilles (cookieKey stable par URL) | ✅ Livré | 1.0.0-beta |
| Sauvegarde nb lignes/page dans cookies | ✅ Livré | 1.0.0-beta |
| Détection automatique mode client/serveur | ❌ Retirée — voir Quick Start | 1.0.3-beta → absente en 3.12.0 |
| Scrollbar horizontale miroir (auto, synchronisée) | ✅ Livré | 1.0.5 |
| Hooks publics JS : acgrRefreshScroll / acgr:refresh-scroll-all | ✅ Livré | 1.0.5 |
| Accessibilité ARIA (grid/row/gridcell, aria-live, aria-expanded...) | ✅ Livré | 1.1.0 |
| Widgets externes brancheables (setValeur/getValeur) | ✅ Livré | 1.1.0 |
| Suppression groupée des lignes épinglées | ✅ Livré | 1.1.0 |
| Événements acgr:log, acgr:charger-sous-grille, acgr:apres-suppression-multiple | ✅ Livré | 1.1.0 |
| Fusion du noyau avec ac_datagrid.php (build.php commun, transport REST configurable via cfg.routes) | ✅ Livré | 3.12.0 |
| Presets (mini/recherche/edition/edition_avancee/complet) | ✅ Livré | 3.12.0 |
| format_conditionnel multi-règles (plusieurs paliers de couleur) | ✅ Livré | 3.12.0 |
| epingle_page_1 (optimisation réseau des lignes épinglées) | ✅ Livré | 3.12.0 |
| Événement acgr:apres-chargement, e.detail.row sur acgr:row-click | ✅ Livré | 3.12.0 |
| Sous-grilles déclaratives (sous_grilles: [...] dans la config du parent) | ✅ Livré | 3.12.0 |
| AbortController — destroy() annule une requête en vol | ✅ Livré | 3.12.0 |
| Adaptateur Laravel | ✅ Livré (démo) | 3.12.0 |
| Correctif 'defaut' de colonne (ignoré côté JS depuis v3.6.7, sans effet réel jusqu'à cette version) | ✅ Livré | 3.13.0 |
| placeholder, aide (édition + en-tête), min/max/step, maxlength — colonnes | ✅ Livré | 3.13.0 |
| decimales — plafond d'affichage sans zéro forcé, découplé de monetaire | ✅ Livré | 3.13.0 |
| defaut_actif — active réellement 'defaut' sur un filtre externe, persistance localStorage | ✅ Livré | 3.13.0 |
| Événement acgr:before_delete (annulable, avant tout appel réseau, suppression simple et groupée) | ✅ Livré | 3.13.0 |
| acgr:log enrichi de detail.donnees | ✅ Livré | 3.13.0 |
| pk — clé primaire explicite, prioritaire sur l'heuristique getPK(), exclue d'office du corps envoyé au backend REST | ✅ Livré | 3.13.0 |
| editable:'creation' — saisissable uniquement à la création, verrouillé et visible en modification | ✅ Livré | 3.13.0 |
| formule — colonnes calculées, recalcul live navigateur, whitelist anti-injection | ✅ Livré | 3.14.0 |
| Adaptateur Symfony | 📋 Planifié | — |
| Publication npm | 📋 Planifié | — |