Au démarrage d’un nouveau document dans Grist, une fois passé la modélisation des tables, un constat rapide s’impose. Elles sont vides ! Or pour tester les vues, les formules, les liaisons, les process d’automatisations ETL (Apache HOP, n8n), les data-visualisations, les widgets… nous avons besoins de produire de la données tout de suite.
Dans ce guide, nous verrons comment générer et injecter automatiquement des données fictives au sein de vos tables Grist. Bien entendu, plusieurs approches sont envisageables, mais nous nous concentrerons spécifiquement sur l’utilisation de la bibliothèque Faker.js dans un Custom Widget Grist.
Préambule
Motivations
Pourquoi générer des données fictives ?
Par défaut, pour tester nos documents Grist en cours de développement, encore à l’état de maquette/prototype, nous saisissons souvent les données manuellement. Mais soyons honnêtes, c’est une tâche longue, fastidieuse et, la plupart du temps qui limite la pertinence de nos tests. Imaginez que vous deviez construire un tableau de bord intégrant une dizaine d’indicateurs clés (KPI) avec un suivi mensuel. Pour tester la pertinence de vos datavisualisations sur un historique de 5 ans, vous êtes parti pour créer 1200 lignes (10 indicateurs x 12 mois x 10 ans). Toujours motivé ? Se contenter de saisies manuelles est non seulement chronophage et fastidieux, mais limite souvent la qualité de nos tests.
De plus, même si la donnée générée est par définition fictive, elle se doit d’être réaliste pour être pleinement exploitable. Des valeurs aléatoires incohérentes ne peuvent que nuire à l’interprétation métier. Des jeux de données crédibles, respectant les contraintes entre tables, sont le seul moyen de valider réellement le comportement de nos prototypes.
L’utilisation de données synthétiques (a.k.a. mock data) est une pratique fondamentale pour concevoir des solutions de qualité. Elle permet de dissocier le développement de l’application de la disponibilité des données réelles.
-
Développement agile : Permettre de découpler les phases de développement de l’acquisition ou du nettoyage des données réelles, et ainsi accélérer le cycle de développement. Cette approche favorise un déploiement continu et une boucle d’itération rapide avec les parties prenantes, réduisant ainsi le time-to-market.
-
Prototypage : Démontrer le potentiel d’une interface, d’une data-visualisation aux parties prenantes avec des données cohérentes et visuellement attractives, facilitant l’adhésion au projet.
-
Validation des flux de travail : Tester des processus complexes (automatisations, déclencheurs, formules liées) avec des volumes de données représentatifs sans attendre l’accumulation d’un historique réel.
-
Validation design (UI/UX) : Visualisez vos widgets (cartes, graphiques, calendriers) avec une volumétrie réaliste. Identifier les goulots d’étranglement ou les bugs d’interface (troncature de texte, casse de mise en page) qui n’apparaissent qu’avec des jeux de données larges ou atypiques. Évaluer la densité d’information (Est-ce que 50 entrées dans une vue brisent la navigation ?)
-
Sécurité et conformité : Éviter l’exposition de données sensibles lors des phases de développement, de test ou de démonstration.
-
Test de robustesse : Vérifiez comment vos formules de colonnes réagissent avec des chaînes de caractères longues.
Comment générer de la donnée synthétique ?
Pour injecter des données automatiqumeent dans une ou plusieurs tables Grist, deux stratégies sont possibles :
L’approche externe : solution ETL (Extract, Transform, Load)
Ici, vous utilisez des solutions tierces à Grist (comme Apache HOP, n8n, Make, voir même des applications scripts Python, PHP, Go, Java…) pour générer des flux de données au format (CSV/JSON), qui seront ensuite intégrés dans des tables Grist
- soit par l’emploi de l’API Rest de Grist,
- soit par l’alimentation directe d’un document Grist, en tant que base de données SQLite, depuis une simple requête SQL.
Cette approche consiste à concevoir des pipelines de données robustes capables de traiter des volumes importants et de gérer des interdépendances complexes entre plusieurs tables Grist.
Vous avez dit ETL ?
L’acronyme ETL (Extract, Transform, Load) désigne un processus de production et de transfert des données, qui s’orchestre en trois phases clés :
- Extract (Extraction) : C’est la phase de collecte. Elle consiste à extraire des données brutes depuis des sources hétérogènes : fichiers plats (CSV, JSON, Excel), bases de données (SQL, NoSQL), logs applicatifs ou services web via API.
- Transform (Transformation) : C’est l’étape de mise en conformité. Les données sont nettoyées, restructurées et enrichies pour répondre aux exigences métier. C’est ici que l’on assure la qualité, la cohérence et la pertinence de l’information avant son exploitation.
- Load (Chargement) : C’est l’étape finale. Les données, désormais fiables et formatées, sont injectées dans leur système de destination (entrepôt de données, application métier comme Grist, ou outil de visualisation).
Un « pipeline de données » désigne un processus automatisé de traitement de flux de données, constitué par ces trois phases clés ETL. La plupart des outils d’ETL modernes permettent de concevoir ces pipelines de manière graphique et visuelle, transformant des lignes de code complexes en une série d’étapes intuitives.
— Exemple de pipeline ETL sous Apache HOP : une représentation visuelle claire du cheminement de la donnée.
L’approche intégrée : génération depuis un custom widget Grist avec Faker.js
Contrairement aux solutions ETL qui traitent la donnée depuis l’extérieur, cette approche consiste à déployer un Custom Widget directement au sein de votre document Grist. Ce widget agit comme un véritable outil d’administration. Il vous permet de piloter la production et l’injection de jeux de données synthétiques à la demande, sans jamais quitter votre document Grist. Gain de temps notable pour la configuration des accès, et à l’usage pour générer la donnée fictives à la volée.
— Exemple d’un widget pour générer de la données fictives dans une table Grist
S’il est techniquement possible de développer votre propre générateur en utilisant des fonctions aléatoires basiques comme Math.random(), cette approche atteint rapidement ses limites dès lors que vous cherchez de l’efficience et de la crédibilité métier. C’est ici que la bibliothèque Faker.js se rend (très rapidement) indispensable.
Faker.js est une bibliothèque JavaScript open-source qui permet de générer des données fictives (mock data) et réalisites à la volée. Elle est conçue pour être utilisée dans des environnements Node.js ou directement dans un navigateur web, comme c’est le cas pour un custom widget. Elle transforme la tâche fastidieuse de création de données synthéthiques en un processus automatisé et rationalisé, et apporte de nombreux avantages, dont:
-
Au-delà de l’aléatoire, la sémantique : Là où un randomiseur classique se contente de générer des suites de caractères ou des nombres sans signification, Faker.js produit des données typées et réalistes (noms de personnes, adresses postales, emails structurés, dates cohérentes, données financières).
-
Respect de la réalité du terrain : En générant des données qui reflètent la complexité du réel (formats de téléphone, codes postaux, intitulés de postes), vous testez votre document Grist dans des conditions quasi-réelles. Cela permet d’ajuster vos formules, vos vues et vos graphiques comme si vous manipuliez des données terrain.
-
Adéquation structurelle : Faker.js s’intègre nativement à la structure de vos colonnes. Une version évoluée de votre widget peut par exemple interroger le schéma de votre table pour mapper automatiquement les types attendus avec les générateurs appropriés, garantissant ainsi que les données injectées sont immédiatement exploitables.
-
Gain de productivité : Cette approche supprime le besoin d’infrastructure externe. En couplant l’interface visuelle du widget avec la puissance de Faker.js, vous réduisez le cycle de prototypage à un simple clic : vous définissez vos besoins, le script peuple vos tables, et vous testez instantanément l’ergonomie de vos outils sans avoir à manipuler de fichiers CSV ou gérer des pipelines complexes.
Bien que ce guide illustre l’utilisation de Faker.js dans un custom widget, gardez à l’esprit que cette bibliothèque n’est qu’une solution parmi d’autres. Si vous souhaitez réduire votre dépendance à des librairires tierces, rien n’empêche de concevoir son propre système de génération de données aléatoires (randomizer)
Premier cas simple, l’alimentation d’une seule table Grist
Pour apprécier l’architecture d’un service Grist Faker Data, nous allons commencer par l’implémentation d’un cas simple, en construisant un Custom Widget dédié à l’alimentation automatique d’une unique table.
On va voir par étape :
- Modélisation de la table cible à alimenter
- Conception d’un service dédié à la génération de donnée (pour une ligne)
- Préparation d’un collection d’objets cohérents (payload) pour l’alimentation de la table
- Injection massive via les bulks actions de l’API de Grist
1. Modélisation de la table STRUCTURES
Pour illustrer notre exemple, nous allons créer une table STRUCTURES, avec la modélisation (simpliste) suivante.
@grist.UserTable
class STRUCTURES:
CODE = grist.Text()
NOM = grist.Text()
ADRESSE = grist.Text()
EMAIL = grist.Text()
TELEPHONE = grist.Text()
STATUT = grist.Text()
2. Services de génération de données (Generators)
Pour génerer une ligne dans cette table, nous allons concevoir un service dédié dans notre custom widget en JavaScript. Ce service, en tant que moteur de génération de données, doit respecter le schéma de la table STRUCTURES.
L’idée ici est de proposer une architecture qui centralise l’ensemble des randomizers (générateurs de donnés synthethiques) dans un unique objet JavaScript, que l’on nomme sans grand mystère Generators.
<script type="module">
const { fakerFR as faker } = await import('https://esm.sh/@faker-js/faker');
const Generators = {
// Génère un identifiant (ex: STR-8492)
code: () => faker.helpers.replaceSymbols('STR-####'),
// Génère une identité
structureIdentity: () => {
const type = faker.helpers.arrayElement(["Collège", "Lycée", "École"]);
const lastName = faker.person.lastName();
return {
nom: `${type} ${lastName}`,
lastName: lastName
};
},
// Génére un email dépendant d'un nom passé en paramètre
email: (lastName) => faker.internet.email(
{firstName: 'contact',
lastName,
provider: 'ac-academie.fr'}
),
// Génère une adresse postale fictive complète
adresse: () => `${faker.location.streetAddress()}, ${faker.location.zipCode()} ${faker.location.city()}`,
// Génère un numéro de téléphone qui commence par 06
telephone: () => faker.phone.number('06 ## ## ## ##')
// Génére un statut administratif pour la structure
statut: () => faker.helpers.arrayElement([
"Public",
"Privé sous contrat",
"Privé hors contrat"
])
};
// Exemple d'utlisation du service Generators
console.log(Generators.code();)
console.log(Generators.telephone();)
</script>
L’architecture de ce service repose sur un objet littéral JavaScript qui offre deux avantages :
- l’isolation de la logique de génération (chaque colonne possède sa propre logique de génération)
- et l’extensibilité (il est aisé d’ajouter ou de modifier la logique pour chaque champ).
Et si on souhaite se passer de Faker.js ?
Bien que l’exemple ci-dessus s’appuie sur la bibliothèque Faker.js, cette architecture est totalement agnostique. Pour s’affranchir de toute dépendance, vous pouvez remplacer les méthodes de Faker par vos propres implémentations soit en injectant des listes de données (CSV/JSON) ou soit par l’emploi de fonctions de génération personnalisées (randomizer).
Ci-dessous un exemple équivalent en pure Vanilla.
const Generators = {
// Utilitaire interne
_getRandomRange: (min, max) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
// Génère un identifiant unique formaté
code: function() {
const randomNumbers = this._getRandomRange(1000, 9999);
return `STR-${randomNumbers}`;
},
// Génère un numéro de téléphone qui commence par 06
telephone: function() {
const blocks = Array.from(
{ length: 4 },
() => this._getRandomRange(10, 99)
);
return `06 ${blocks.join(' ')}`;
}
};
Assurer l’unicité d’un item généré (clé primaire)
Imaginons l’amélioration du service de génération du code d’une structure, pour assurer son unicité en tant que clé primaire de la table. Remarquons, qu’il est aisé d’intervenir sur la fonction en charge de la génération du code, sans risquer de polluer les autres méthodes.
Pour assurer l’unicité, il suffit de mémoriser ce qui a déjà été généré. On peut le faire avec un simple Set pour stocker les codes déjà créés, de la manière suivante.
<script type="module">
const faker = await import('https://esm.sh/@faker-js/faker');
// Registre des codes déjà utilisés lors de la génération
const storeCode = new Set();
const Generators = {
// Génère un identifiant unique (ex: STR-8492)
code: () => {
let newCode;
do {
newCode = faker.helpers.replaceSymbols('STR-####');
} while (storeCode.has(newCode)); // Répète tant que le code existe
storeCode.add(newCode); // Enregistre le nouveau code dans le registre
return newCode;
}
// ... méthodes précédentes ... //
};
</script>
Localisation des données générées (i18n)
La bibliothèque Faker.js facilite grandement la localisation (i18n) de vos données de test. Bien que l’instance par défaut soit configurée en anglais, elle propose plus de 70 locales prédéfinies : comme fakerFR pour la France, fakerDE pour l’Allemagne ou fakerES pour l’Espagne.
Cela se fait au niveau de l’import de la manière suivante.
import {fakerFR as faker} from 'https://esm.sh/@faker-js/faker'
Il suffit d’importer l’instance spécifique correspondant à la région souhaitée au lieu de l’instance générique pour que les noms, adresses, numéros de téléphone et formats de données s’adaptent automatiquement aux conventions culturelles et linguistiques locales.
Déterminisme (seed)
Le caractère aléatoire de Faker.js est idéal pour diversifier les données, mais il devient un obstacle lorsque la reproductibilité est nécessaire. La fonction faker.seed() permet de fixer l’état interne du générateur, garantissant que les mêmes appels produisent les mêmes génération à chaque exécution. Bien que son usage, son plus adapté dans un contexte d’exécution de tests unitaires, il peut être intérressant de fixer un résultat dans notre cas d’usage.
Pour ce faire, la graine doit être définie avant tout appel aux méthodes de génération à l’aide d’un entier.
import { fakerFR as faker } from '@faker-js/faker';
// Fixer la graine
faker.seed(1234);
// Ces appels produiront toujours les mêmes valeurs
console.log(faker.person.fullName()); // "Marie Lefebvre"
console.log(faker.location.city()); // "Nantes"
Remarquez, vous pouvez définir la graine depuis un identifiant pour isoler les séquences de données par entité (i.e. pour chaque ligne de la table).
function generateDataForStructure(id) {
// Utilise l'ID pour garantir que cet entité génère toujours les mêmes données
faker.seed(parseInt(id));
return {
code: `STR-${id}`,
nom: faker.company.name()
};
}
3. Génération d’un collection d’objets (payload) pour l’alimentation de la table
L’idée est de passer du service de génération des données pour une entité, à la préparation d’une collection d’entités cohérentes à inserer dans la table STRUCTURES, et conforme au format de l’API Javascript Grist. C’est à dire que l’on prepare en une seule fois, l’intégralité des lignes à injecter dans la table.
La fonction ci-dessous generateStructuresPayload construit une collection d’objet à l’aide du service Generators, où chaque clé est une colonne et chaque valeur un tableau. Le respect de cette structure par colonne est indispensable pour opérer des opérations de masse (bulk actions) dans Grist.
function generateStructuresPayload(numberOfRecordsToGenerate) {
const payload = {
CODE: [],
NOM: [],
ADRESSE: [],
EMAIL: [],
TELEPHONE: [],
STATUT: []
};
for (let i = 0; i < numberOfRecordsToGenerate; i++) {
const identity = Generators.structureIdentity();
payload.CODE.push(Generators.code());
payload.NOM.push(identity.nom);
payload.ADRESSE.push(Generators.adresse());
payload.EMAIL.push(Generators.email(identity.lastName));
payload.TELEPHONE.push(Generators.telephone());
payload.STATUT.push(Generators.statut());
}
return payload;
}
Format par colonnes des jeux de données pour les bulk actions dans Grist
Grist utilise une architecture par colonnes pour ses opérations en masse (bulk Actions). Au lieu d’envoyer une liste d’objets (ligne par ligne) augmentant d’autent le nombre de requêtes HTTP, on va fournir un objet unique où chaque clé correspond au nom de votre colonne et chaque valeur est un tableau contenant l’ensemble des données pour cette colonne. On a sinsi une seule requête HTTP avec un parsing JSON optimal côté serveur pour alimenter la table.
Ci-dessous un exemple de jeux de données au format par colonne. Dans la matrice, chaque colonne correspond à une entité.
{ "CODE": ["STR-6196", "STR-3788", "STR-9079"], "NOM": ["École Paris", "Lycée Clement", "École Lemoine"], "ADRESSE": ["9 Bd Oberkampf, 96698 Le Mans", "59 Av Grands Augustins, 78500 Aix-en-Pce", "9741 Bd St-Denis, 62413 Créteil"], "EMAIL": ["contact_Paris8@ac-academie.fr", "contact_Clement@ac-academie.fr", "contact_Lemoine@ac-academie.fr"], "TELEPHONE": ["01 06 29 68 27", "01 46 15 01 61", "03 83 36 67 02"], "STATUT": ["Privé hors contrat", "Public", "Privé sous contrat"] }
Validation de l’intégrité dimensionnelle du payload
Bien que le format requis par les bulk actions de Grist soit relativement simple, sa structure exige une rigueur absolue. Pour éviter les erreurs d’exécution lors d’opérations sur de grands volumes de données, il est recommandé d’implémenter une routine de contrôle garantissant la cohérence dimensionnelle du payload avant l’envoi.
Le principe est de vérifier que chaque colonne transmise contient exactement le même nombre d’éléments, correspondant au nombre d’enregistrements attendus.
/**
* Valide que toutes les colonnes du payload possèdent la même longueur.
*
* @param {Object} payload - L'objet contenant les colonnes à envoyer.
* @param {number} expectedCount - Le nombre d'items attendu par colonne.
* @throws {Error} Si une incohérence est détectée.
*/
function validatePayload(payload, expectedCount) {
const columns = Object.keys(payload);
for (const col of columns) {
if (payload[col].length !== expectedCount) {
throw new Error(`[ERREUR] La colonne ${col} contient ${payload[col].length} éléments au lieu de ${expectedCount}.`);
}
}
}
4. Injection massive via les bulks actions de l’API de Grist
Cette dernière étape finalise le cycle de vie des données fictives générés, qui consiste a effectivement envoyer les données dans la table Grist.
L’utilisation des bulk actions permet d’effectuer des opérations atomiques (tout ou rien) et d’optimiser les performances réseau en une seule requête HTTP. Pour s’assurer que la table soit propre, on prépare une transaction Grist qui combine plusieurs actions :
- en premier, on récupère la liste des ids de la table cible
- en second, on purge la table si cette dernière n’est pas vide
- et enfin, on alimente la table avec les données fictives précédement générées et structurés au foramt colonne (payload).
async function injectData(tableId, payload) {
if (!payload || payload.length === 0) {
throw new Error("Le payload est invalide.");
}
try {
const actions = [];
// Récupération des IDs existants
const data = await grist.docApi.fetchTable(tableId).catch(() => ({ id: [] }));
const ids = data.id || [];
// Action pour vider la table si elle n'est pas vide
if (ids.length > 0) {
actions.push(["BulkRemoveRecord", tableId, ids]);
}
// Action pour insérer les nouvelles données
// Attention !!! payload doit être au format colonne
// {col1: ['a', ...], col2: ['b', ...]}
actions.push([
"BulkAddRecord",
tableId,
Array(payload.length).fill(null),
payload
]);
// Exécution atomique des bulks actions
await grist.docApi.applyUserActions(actions);
console.log(`Table '${tableId}' mise à jour avec succès avec ${payload.length} enregistrements.`);
} catch (error) {
throw new Error(`Erreur lors de l'injection des données : ${error.message}`);
}
}
Implémentation du service de génération de données fictives dans un custom widget Grist
L’intégration de notre générateur de données fictives au sein d’un document Grist prend la forme d’un fichier HTML unique contenant la logique de génération (Generators), l’emploi des bulks actions de l’API Grist pour faire de l’insertion massive (grist.docApi.applyUserActions(actions)) et en bonus une interface utilisateur minimaliste conforme au DSFR implémenté depuis un simple CDN.
— Exemple d’un custom widget pour générer de la données fictives dans une table Grist
<!DOCTYPE html>
<html lang="fr" data-fr-scheme="light">
<head>
<meta charset="UTF-8">
<title>Fixture Grist - DSFR</title>
<link href="https://cdn.jsdelivr.net/npm/@gouvfr/dsfr@1.14.3/dist/dsfr/dsfr.min.css" rel="stylesheet">
<script src="https://docs.getgrist.com/grist-plugin-api.js"></script>
<style>
.fr-container {
padding-top: 2rem;
max-width: 1000px;
}
</style>
</head>
<body>
<div class="fr-container fr-py-2w">
<div class="fr-grid-row">
<div class="fr-col-12">
<h1 class="fr-h6 fr-mb-2w">Grist Faker Datas :: Générateur de données pour la table STRUCTURES</h1>
<p class="fr-text--xs">
Notez que la table STRUCTURES doit respecter le modèle suivant : STRUCTURES (<u>CODE</u>, NOM, ADRESSE, EMAIL, TELEPHONE, STATUT)
</p>
<div class="fr-search-bar fr-search-bar--lg" id="search-1">
<input class="fr-input" placeholder="Volume à générer" type="number" id="volume" value="10">
<button class="fr-btn" id="btn-run">Générer</button>
</div>
<div id="alert-container" class="fr-mt-2w"></div>
</div>
</div>
</div>
<script type="module">
import { fakerFR as faker } from 'https://esm.sh/@faker-js/faker';
const TARGET_TABLE = "STRUCTURES";
const GENERATORS_STORE_CODE = new Set();
/**
* Service utilitaire pour générer des données fictives pour la table STRUCTURES
*/
const Generators = {
code: () => {
let newCode;
do {
newCode = faker.helpers.replaceSymbols('STR-####');
} while (GENERATORS_STORE_CODE.has(newCode));
GENERATORS_STORE_CODE.add(newCode);
return newCode;
},
structureIdentity: () => {
const type = faker.helpers.arrayElement(["Collège", "Lycée", "École"]);
const lastName = faker.person.lastName();
return {
nom: `${type} ${lastName}`,
lastName: lastName
};
},
email: (lastName) => faker.internet.email({firstName: 'contact', lastName, provider: 'ac-academie.fr'}),
adresse: () => `${faker.location.streetAddress()}, ${faker.location.zipCode()} ${faker.location.city()}`,
telephone: () => {
const prefix = faker.helpers.arrayElement(['06', '07']);
const rest = Array.from({ length: 8 }, () => Math.floor(Math.random() * 10)).join('');
const fullNumber = prefix + rest;
return fullNumber.match(/.{1,2}/g).join(' ');
},
statut: () => faker.helpers.arrayElement(["Public", "Privé sous contrat", "Privé hors contrat"]),
/**
* Valide que chaque colonne du payload contient exactement le nombre d'éléments attendu.
*/
_validatePayload: (payload, expectedCount) => {
const columns = Object.keys(payload);
for (const col of columns) {
if (payload[col].length !== expectedCount) {
throw new Error(`[ERREUR] La colonne ${col} contient ${payload[col].length} éléments au lieu de ${expectedCount}.`);
}
}
},
/**
* Génère un payload conforme au format bulk actions pour Grist contenant plusieurs structures (lignes)
*/
structuresPayloadGrist(numberOfStructures) {
const payload = {CODE: [], NOM: [], ADRESSE: [], EMAIL: [], TELEPHONE: [], STATUT: []};
for (let i = 0; i < numberOfStructures; i++) {
const identity = this.structureIdentity();
payload.CODE.push(this.code());
payload.NOM.push(identity.nom);
payload.ADRESSE.push(this.adresse());
payload.EMAIL.push(this.email(identity.lastName));
payload.TELEPHONE.push(this.telephone());
payload.STATUT.push(this.statut());
}
// Validation interne du payload (pour exemple, car pas utile dans ce cas)
this._validatePayload(payload, numberOfStructures);
return payload;
}
};
/**
* Injecte des données dans une table Grist en purgeant au préalable le contenu existant.
*/
async function serviceGristInjectData(tableId, payload) {
// Calcul du nombre d'éléments via la première colonne disponible
const recordCount = Object.values(payload)[0]?.length || 0;
if (recordCount === 0) throw new Error("Le payload est vide.");
// Pour stocker les bulks actions Grist
const actions = [];
// Ajout de l'action de purge de la table si nécessaire
const data = await grist.docApi.fetchTable(tableId).catch(() => ({ id: [] }));
const ids = data.id || [];
if (ids.length > 0) {
actions.push([
"BulkRemoveRecord",
TARGET_TABLE,
ids
]);
}
// Ajout de l'action d'insertion de données
actions.push([
"BulkAddRecord",
TARGET_TABLE,
Array(recordCount).fill(null),
payload
]);
// Exécution des actions
await grist.docApi.applyUserActions(actions);
}
document.addEventListener('DOMContentLoaded', () => {
grist.ready({ requiredAccess: 'full' });
let isProcessing = false;
const btnRun = document.getElementById('btn-run');
const alertContainer = document.getElementById('alert-container');
const ui = {
alert: (type, msg) => {
alertContainer.innerHTML = `<div class="fr-alert fr-alert--${type}"><p>${msg}</p></div>`;
}
};
btnRun.addEventListener('click', async () => {
if (isProcessing) return;
const count = parseInt(document.getElementById('volume').value) || 0;
ui.alert('info', 'Traitement en cours...');
isProcessing = true;
try {
const payload = Generators.structuresPayloadGrist(count);
await serviceGristInjectData(TARGET_TABLE, payload);
ui.alert('success', `Succès : ${count} structures générées.`);
} catch (err) {
ui.alert('error', `Erreur : ${err.message}`);
} finally {
isProcessing = false;
}
});
});
</script>
</body>
</html>
Cas d’usage : alimentation de plusieurs tables Grist
Dans un environnement réel, vos données sont rarement isolées. La puissance de Grist réside dans ses relations (colonnes de type Reference). Alimenter plusieurs tables nécessite donc une stratégie de gestion des dépendances, afin de garantir l’intégrité des relations (notamment via les colonnes de type Référence).
Dans ce cas, il faut travailler avec les Ids des éléments référencés, mais le service de génération des données fictives repose sur la même architecture : Generators > Préparation d’un payload conforme > Injection via bulk actions Grist.
Ca mérite un autre tuto, non ?
Conclusion
La génération de données fictives (mock data) au cœur de vos documents Grist ne doit pas être perçue comme une étape optionnelle, mais bien comme un accélérateur de développement. En éliminant la pénibilité de la saisie manuelle et en s’affranchissant de la disponibilité immédiate de données réelles ou sensibles, l’utilisation conjointe de Grist et de Faker.js offre un cadre idéal pour concevoir des solutions robustes, dans une démarche plus agile et (idéalement) centrée sur l’expérience utilisateur.
Qu’il s’agisse de valider l’ergonomie d’un tableau de bord, de vérifier vos formules de colonnes dans leurs retranchements ou de sécuriser vos démonstrations, la génération de données synthétiques sémantiquement cohérentes est la clé d’un prototypage réussi. La donnée fictive devient alors le bac à sable parfait où vos idées prennent vie instantanément, garantissant que le jour où vos flux de production réels prendront le relais, votre document Grist sera déjà parfaitement calibrée, testée et prête à l’emploi.
Happy generation !!

