Ajout possibilité de fournir des fonctions spécifiques pour classer les données de certaines colonnes.

This commit is contained in:
Fabrice PENHOËT 2021-09-06 17:25:30 +02:00
parent 75c9efc3b1
commit 66ea27b0fb
6 changed files with 85 additions and 16 deletions

View File

@ -18,9 +18,9 @@ De même l'idée est de rester libre du rendu des données en n'imposant pas de
La première version se contente de récupérer et parser des données présentes dans un fichier CSV via un appel Ajax.
Les données trouvées sont affichées dans un tableau. En option, des colonnes peuvent être indiquées par filtrer les données et/ou les classer.
Il est possible de fournir des fonctions spécifiques pour classer les données de certaines colonnes.
Il reste à ajouter :
- la possibilité de fournir des fonctions spécifiques pour classer les données de certaines colonnes.
- la possibilité de paginer les données.
- la possibilité d'utiliser des sources/formats différents qu'un fichier CSV pour extraire les données.
- la possibilité de spécifier un code HTML autre qu'un tableau pour lister les données.

View File

@ -1,6 +1,6 @@
{
"name": "freedatas2html",
"version": "0.3.5",
"version": "0.3.6",
"description": "Visualization of data from various sources (CSV, API, HTML...) with filters, classification, pagination, etc.",
"main": "index.js",
"scripts": {

View File

@ -7,7 +7,20 @@ const initialise = async () =>
let converter=new freeDatas2HTML();
converter.datasViewElt={ id:"datas" };
converter.datasSelectors=[{ datasFieldNb:3, id:"filtre1"}, { datasFieldNb:4, id:"filtre2"},{ datasFieldNb:5, id:"filtre3", separator:"," }];
converter.datasSortingColumns=[{ datasFieldNb:0 }, { datasFieldNb:1 },{ datasFieldNb:2 }];
const mySort = (a: any, b: any, order: "asc"|"desc" = "asc") =>
{
const values = [ "> 100000", "> 1 et < 100 000", "≤ 1", "Traces", "Inexistant"];
if(order === "desc")
values.reverse();
if(values.indexOf(a) > values.indexOf(b))
return -1;
else if(values.indexOf(a) < values.indexOf(b))
return 1;
else
return 0;
};
converter.datasSortingColumns=[{ datasFieldNb:0 }, { datasFieldNb:1 },{ datasFieldNb:2 }, { datasFieldNb:4 }];
converter.datasSortingFunctions= [{ datasFieldNb:4, sort:mySort}];
converter.datasSourceUrl="http://localhost:8080/datas/elements-chimiques.csv";
await converter.run();
}

View File

@ -8,18 +8,20 @@ import { domElement, selectors, sortingColumns, sortingFunctions } from "./free
export class freeDatas2HTML
{
private _datasViewElt: domElement = { id:"", eltDOM:undefined };
private _datasSourceUrl: string = "";
public datasHTML: string = "";
// Revoir car tous les attributs suivants sont liés aux colonnes/fields des données (créer une classe ?)
private _datasSelectors: selectors[] = [];
private _datasSortingColumns: sortingColumns[] = [];
private _datasSortedColumn: sortingColumns|undefined;
private _datasSortingFunctions: sortingFunctions[] = [];
// Parseur fichier :
private _datasSourceUrl: string = "";
public parseMeta: papaParseMeta|undefined = undefined;
public parseDatas: papaParseDatas[] = [];
public parseErrors: papaParseErrors[] = [];
public datasHTML: string = "";
public stopIfParseErrors: boolean = false;
public static isNaturalNumber(nb)
public static isNaturalNumber(nb: number)
{
return (Number.isInteger(nb) === false || nb < 0) ? false : true;
}
@ -90,6 +92,30 @@ export class freeDatas2HTML
return this._datasSortingColumns;
}
// Attention : une fonction de classement peut aussi bien servir à une colonne triable, qu'à une colonne servant à filtrer les données
set datasSortingFunctions(sortingFunctions: sortingFunctions[])
{
this._datasSortingFunctions=[];
for(let i = 0; i < sortingFunctions.length; i++)
{
if(freeDatas2HTML.isNaturalNumber(sortingFunctions[i].datasFieldNb) === false)
console.error(errors.needNaturalNumber);
else
this._datasSortingFunctions.push(sortingFunctions[i]);
}
}
// Retourne la fonction spécifique de classement associée à une colonne
public getSortingFunctionForField(datasFieldNb: number) : sortingFunctions|undefined
{
for(let i in this._datasSortingFunctions)
{
if(this._datasSortingFunctions[i].datasFieldNb === datasFieldNb)
return this._datasSortingFunctions[i];
}
return undefined;
}
public async parse(): Promise<any>
{
const converter=this;
@ -172,7 +198,6 @@ export class freeDatas2HTML
let checkedValues=this.parseDatas[row][colName].split(this._datasSelectors[i].separator as string);
for(let i in checkedValues)
{
let checkedValue=checkedValues[i].trim();
if(checkedValue !== "" && values.indexOf(checkedValue) === -1)
values.push(checkedValue);
@ -181,6 +206,9 @@ export class freeDatas2HTML
}
if(values.length > 0)
{
if(this.getSortingFunctionForField(this._datasSelectors[i].datasFieldNb) !== undefined)
values.sort(this.getSortingFunctionForField(this._datasSelectors[i].datasFieldNb)!.sort);
else
values.sort(compare());
this._datasSelectors[i].name=colName;
this._datasSelectors[i].values=values;
@ -220,7 +248,7 @@ export class freeDatas2HTML
{
const converter=this;
this._datasViewElt.eltDOM.innerHTML=this.datasHTML;
// Ici car il faut que la tableau soit déjà dans le DOM pour écouter les clics
// Ici, car il faut que le tableau soit déjà dans le DOM pour "mettre sous écoute" les clics
if(this._datasSortingColumns.length > 0)
{
let getTableTh=document.querySelectorAll("table th");
@ -233,7 +261,7 @@ export class freeDatas2HTML
htmlContent="<a href='#freeDatas2HTMLSorting"+datasFieldNb+"' id='freeDatas2HTMLSorting"+datasFieldNb+"'>"+htmlContent+"</a>";
getTableTh[datasFieldNb].innerHTML=htmlContent;
let sortingElement=document.getElementById("freeDatas2HTMLSorting"+datasFieldNb);
sortingElement!.addEventListener('click', function(e)
sortingElement!.addEventListener("click", function(e)
{
e.preventDefault();
let order=converter.datasSortingColumns[i].order ;
@ -268,6 +296,13 @@ export class freeDatas2HTML
{
const col=fields[this._datasSortedColumn.datasFieldNb];
const colOrder=this._datasSortedColumn.order;
// Une fonction spécifique de classement a-t-elle été définie ?
if(this.getSortingFunctionForField(this._datasSortedColumn.datasFieldNb) !== undefined)
{
let myFunction=this.getSortingFunctionForField(this._datasSortedColumn.datasFieldNb);
datas.sort( (a, b) => { return myFunction!.sort(a[col], b[col], colOrder); });
}
else
datas.sort( (a, b) => compare( {order: colOrder} )(a[col], b[col]));
}

View File

@ -15,3 +15,8 @@ export interface sortingColumns
datasFieldNb: number;
order?: "asc"|"desc"|undefined;
}
export interface sortingFunctions
{
datasFieldNb: number;
sort(a: any,b: any, order?: "asc"|"desc"): number; // cf. https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
}

View File

@ -41,7 +41,6 @@ describe("freeDatas2HTML", () =>
expect(converter.datasSelectors[0].id).toEqual("selector2");
});
// Utile pour tester les valeurs fournies pour les numéros de colonne
it("Doit retourner un booléen indiquant si un nombre est naturel ou non.", () =>
{
expect(freeDatas2HTML.isNaturalNumber(-1)).toBeFalse();
@ -66,6 +65,23 @@ describe("freeDatas2HTML", () =>
{
expect(() => { return converter.datasSourceUrl=" "; }).toThrowError(errors.needUrl);
});
it("Doit me retourner la fonction associée à une colonne, de manière à ce qu'elle soit utilisable pour comparer deux valeurs.", () =>
{
// Fonction volontairement basique, car ce n'est pas la fonction que l'on teste ici, mais le fait que l'on puisse l'utiliser !
const simpleSort = (a: any, b: any) =>
{
if(a < b)
return 1;
else if(a > b)
return -1;
else
return 0;
};
converter.datasSortingFunctions=[{ datasFieldNb:0, sort:simpleSort }];
expect(converter.getSortingFunctionForField(0)).toBeDefined();
expect([7,9,3,5].sort(converter.getSortingFunctionForField(0).sort)).toEqual([9,7,5,3]);
});
});
describe("Parsage du fichier et création du tableau de données", () =>