Ajout création des listes <select> qui permettront de filtrer les données du fichier

This commit is contained in:
Fabrice PENHOËT 2021-08-11 15:24:00 +02:00
parent 2ae5839aa9
commit 1cf6710c07
6 changed files with 185 additions and 123 deletions

View File

@ -10,6 +10,8 @@
</head>
<body>
<h1>freeCSV2HTML</h1>
<p id="filtre1"></p>
<p id="filtre2"></p>
<article id="datas">
<p>Si tout se passe bien, ce texte sera remplacé par un tableau de données extraites du fichier csv.</p>
</article>

View File

@ -6,4 +6,5 @@ module.exports =
needNaturalNumber: "Merci de fournir un nombre entier supérieur ou égal à zéro pour désigner chaque colonne.",
needUrl: "Merci de fournir l'url du fichier CSV à parser.",
parserFail: "La lecture des données du fichier a échoué.",
selectorFieldNotFound: "Au moins une des colonnes devant servir à filtrer les données n'existe pas dans le fichier.",
};

View File

@ -6,6 +6,7 @@ const initialise = async () =>
{
let converter=new freeCSV2HTML();
converter.datasViewElt={ id:"datas" };
converter.datasSelectors=[{ colCSV:3, id:"filtre1"},{ colCSV:4, id:"filtre2"}];
converter.datasSourceUrl="http://localhost:8080/datas/elements-chimiques.csv";
await converter.run();
}
@ -14,77 +15,4 @@ const initialise = async () =>
console.error(e);
}
}
initialise();
/*
public async setDatasUrl(url: string) : Promise<any> // pas de setter classique à cause asynchrone
{
const converter=this; // nécessaire pour éviter confusion avec this à l'intérieur de XMLHttpRequest
await fetch(url).then(function(response)
{
if(response.ok)
{
converter._datasUrl=url;
}
})
if(converter._datasUrl!=="")
return true;
else
return false;
}
/*
let req = new XMLHttpRequest();
req.open("GET", url);
req.addEventListener("load", function ()
{
if (req.status >= 200 && req.status < 400)
{
converter._datasUrl=url;
return true;
}
else
{
console.error("Fichier non trouvé");
return false;
}
});
}*/
/* Exemple d'appelle complet à papaparse :
Papa.parse(url, {
delimiter :"",
newline: "",
quoteChar:'"',// à proposer en option
escapeChar:"",// idem
header:true,
transformHeader:"",
dynamicTyping:false,
preview:0,
encoding:"",
worker:true,
comments:"",
step:"",// prévoir en option pour très gros fichiers ?
complete: function(results :any)
{
console.log(results.meta);
console.log(results.data[35]);
console.log(results.errors);
},
error:function(error :any)
{
console.log(error);
},
download: true,// nécessaire pour un accès via "http..."
downloadRequestBody:false,
skipEmptyLines: true,
chunk:"",
fastMode:"",
beforeFirstChunk:"",
withCredentials:false,
transform: "", // à revoir
delimitersToGuess:""
});
* // https://www.papaparse.com/docs#config*/
initialise();

View File

@ -11,6 +11,8 @@ interface domElement
interface selectors extends domElement
{
colCSV: number;
name?: string;
values? : string[];
}
export class freeCSV2HTML
@ -20,7 +22,6 @@ export class freeCSV2HTML
private _datasSourceUrl: string = "";
private _datasSelectors: selectors[] = [];
// Données reçues ou générées suite parsage du fichier CSV :
public parseMeta: papaParseMeta|undefined = undefined;
public parseDatas: papaParseDatas[] = [];
public parseErrors: papaParseErrors[] = [];
@ -38,7 +39,7 @@ export class freeCSV2HTML
}
}
// Pas plus de tests car nombreux protocoles possibles pour l'url du fichier, pas uniquement http(s). À revoir.
// Pas plus de tests + poussés, car nombreux protocoles possibles pour l'url du fichier. À revoir.
set datasSourceUrl(url: string)
{
if(url.trim().length === 0)
@ -47,35 +48,34 @@ export class freeCSV2HTML
this._datasSourceUrl=url.trim();
}
// La validité du numéro de colonne ne peut être vérifée qu'après le parsage.
set datasSelectors(selectors: selectors[])
set datasSelectors(selectionElts: selectors[])
{
let checkContainerExist: HTMLElement|null;
for(let i = 0; i < selectors.length; i++)
for(let i = 0; i < selectionElts.length; i++)
{
checkContainerExist=document.getElementById(selectors[i].id);
checkContainerExist=document.getElementById(selectionElts[i].id);
if(checkContainerExist === null)
throw new Error(errors.elementNotFound+selectors[i].id);
else if(Number.isInteger(selectors[i].colCSV) === false || selectors[i].colCSV < 0)
throw new Error(errors.needNaturalNumber);
throw new Error(errors.elementNotFound+selectionElts[i].id);
else if(Number.isInteger( selectionElts[i].colCSV) === false || selectionElts[i].colCSV < 0)
throw new Error(errors.needNaturalNumber); // La validité réelle du numéro de colonne ne peut être vérifée qu'après le parsage.
else
selectors[i].eltDOM=checkContainerExist;
selectionElts[i].eltDOM=checkContainerExist;
}
this._datasSelectors=selectors;
this._datasSelectors= selectionElts;
}
public async parse(): Promise<any>
{
const converter=this; // nécessaire pour éviter confusion avec le "this" de la promise
const converter=this;
return new Promise((resolve,reject) =>
{
if(this._datasSourceUrl !== "" )
if(converter._datasSourceUrl !== "" )
{
Papa.parse(this._datasSourceUrl,
Papa.parse(converter._datasSourceUrl,
{
quoteChar:'"', // à proposer en option ?
header:true,
complete: function(results :any)
quoteChar: '"',
header: true,
complete: function(results :any)
{
converter.parseMeta=results.meta;
converter.parseErrors=results.errors; // prévoir option bloquant la suite à la moindre erreur trouvée ?
@ -87,11 +87,11 @@ export class freeCSV2HTML
reject(new Error(errors.parserFail));
},
download: true,
skipEmptyLines: true, // à proposer en option ?
skipEmptyLines: true,
});
}
else
resolve(new Error(errors.needUrl));
reject(new Error(errors.needUrl));
});
}
@ -99,37 +99,106 @@ export class freeCSV2HTML
{
if (this._datasViewElt.eltDOM === undefined)
throw new Error(errors.needDatasElt);
if(this._datasSourceUrl === "" )
else if(this._datasSourceUrl === "" )
throw new Error(errors.needUrl);
if(await this.parse() === true)
await this.parse();
if(this.parseDatas.length === 0 || this.parseMeta === undefined || this.parseMeta.fields === undefined)
{
if(this.parseDatas.length === 0 || this.parseMeta === undefined || this.parseMeta.fields === undefined)
{
this._datasViewElt.eltDOM.innerHTML=errors.datasNotFound;
return false;
}
else
{
this.datasHTML="<table><thead>";
for (let i in this.parseMeta.fields)
this.datasHTML+="<th>"+this.parseMeta.fields[i]+"</th>";
this.datasHTML+="</thead><tbody>";
for (let row in this.parseDatas)
{
this.datasHTML+="<tr>";
for(let field in this.parseDatas[row])
{
if( this.parseMeta.fields.indexOf(field) !== -1) // si les erreurs papaParse sont acceptées, il peut y avoir des données en trop : "__parsed_extra"
this.datasHTML+="<td>"+this.parseDatas[row][field]+"</td>";
}
this.datasHTML+="</tr>";
}
this.datasHTML+="</tbody></table>";
this._datasViewElt.eltDOM.innerHTML=this.datasHTML;
return true;
}
this._datasViewElt.eltDOM.innerHTML=errors.datasNotFound;
return false;
}
else
{
// Affichage initial des données du fichier
this.datasHTML="<table><thead>";
for (let i in this.parseMeta.fields)
this.datasHTML+="<th>"+this.parseMeta.fields[i]+"</th>";
this.datasHTML+="</thead><tbody>";
for (let row in this.parseDatas)
{
this.datasHTML+="<tr>";
for(let field in this.parseDatas[row])
{
if( this.parseMeta.fields.indexOf(field) !== -1) // si les erreurs papaParse sont acceptées, il peut y avoir des données en trop : "__parsed_extra"
this.datasHTML+="<td>"+this.parseDatas[row][field]+"</td>";
}
this.datasHTML+="</tr>";
}
this.datasHTML+="</tbody></table>";
this._datasViewElt.eltDOM.innerHTML=this.datasHTML;
// Si demandé, création des listes permettant de filter les données
if(this._datasSelectors.length > 0)
{
// Les colonnes devant servir de filtre existent-elles dans le fichier ?
let selectorsHTML : string [] = [];
for(let i in this._datasSelectors)
{
if(this._datasSelectors[i].colCSV > (this.parseMeta.fields.length-1))
throw new Error(errors.selectorFieldNotFound);
else
{
let values=[], colName=this.parseMeta.fields[this._datasSelectors[i].colCSV];
for (let row in this.parseDatas)
{
if(values.indexOf(this.parseDatas[row][colName]) === -1)
values.push(this.parseDatas[row][colName]);
}
if(values.length > 0)
{
values.sort(); // revoir le problème des chiffres, dates, etc. https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
this._datasSelectors[i].name = colName;
this._datasSelectors[i].values=values;
selectorsHTML[i]="<label for='CSVDatasSelector"+i+"'>"+colName+" : </label><select name='CSVDatasSelector"+i+"' id='CSVDatasSelector"+i+"'><option value='0'>----</option>";
for(let j in values)
selectorsHTML[i]+="<option value='"+(Number(j)+1)+"'>"+values[j]+"</option>";
selectorsHTML[i]+="</select>";
this._datasSelectors[i].eltDOM!.innerHTML=selectorsHTML[i];// je force avec "!", car l'existence de eltDOM a été testée par le setter.
}
// suite : https://stackoverflow.com/questions/19329978/change-selects-option-and-trigger-events-with-javascript
}
}
}
return true;
}
}
}
}
/* Exemple d'appelle complet à papaparse :
Papa.parse(url, {
delimiter :"",
newline: "",
quoteChar:'"',// à proposer en option
escapeChar:"",// idem
header:true,
transformHeader:"",
dynamicTyping:false,
preview:0,
encoding:"",
worker:true,
comments:"",
step:"",// prévoir en option pour très gros fichiers ?
complete: function(results :any)
{
console.log(results.meta);
console.log(results.data[35]);
console.log(results.errors);
},
error:function(error :any)
{
console.log(error);
},
download: true,// nécessaire pour un accès via "http..."
downloadRequestBody:false,
skipEmptyLines: true,
chunk:"",
fastMode:"",
beforeFirstChunk:"",
withCredentials:false,
transform: "", // à revoir
delimitersToGuess:""
});
* // https://www.papaparse.com/docs#config*/

File diff suppressed because one or more lines are too long

View File

@ -112,5 +112,63 @@ describe("freeCSV2HTML", () =>
let txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTML);
});
it("Doit générer une erreur si au moins un des numéros de colonne des sélecteurs ne correspond pas à une des colonne du fichier.", async () =>
{
converter.datasViewElt={ id:"datas" };
converter.datasSourceUrl="http://localhost:9876/datas/elements-chimiques.csv";
converter.datasSelectors=[{ colCSV:0, id:"selector1"},{ colCSV:5, id:"selector2"}];
await expectAsync(converter.run()).toBeRejectedWith(new Error(errors.selectorFieldNotFound));
});
it("Ne doit pas générer d'erreur si tous les numéros de colonne des sélecteurs correspondent pas à une des colonne du fichier.", async () =>
{
converter.datasViewElt={ id:"datas" };
converter.datasSourceUrl="http://localhost:9876/datas/elements-chimiques.csv";
converter.datasSelectors=[{ colCSV:3, id:"selector1"},{ colCSV:4, id:"selector2"}];
await expectAsync(converter.run()).not.toBeRejected();
});
it("Pour chaque sélecteur demandé doit générer un élement <select> listant les valeurs distinctes du fichier, classées par ordre alphabétique.", async () =>
{
converter.datasViewElt={ id:"datas" };
converter.datasSourceUrl="http://localhost:9876/datas/elements-chimiques.csv";
converter.datasSelectors=[{ colCSV:3, id:"selector1"},{ colCSV:4, id:"selector2"}];
await converter.run();
expect(document.getElementById("selector1").innerHTML).toEqual(fixtures.selector1HTML);
expect(document.getElementById("selector2").innerHTML).toEqual(fixtures.selector2HTML);
});
it("Le choix d'un option dans un des sélecteurs doit modifier le contenu du tableau pour ne garder que les données valides pour ce choix et afficher toutes les données si sélection 0.", async () =>
{
converter.datasViewElt={ id:"datas" };
converter.datasSourceUrl="http://localhost:9876/datas/elements-chimiques.csv";
converter.datasSelectors=[{ colCSV:3, id:"selector1"},{ colCSV:4, id:"selector2"}];
await converter.run();
let selectElement = document.getElementById("CSVDatasSelector0") as HTMLInputElement;
selectElement.value="4";
selectElement.dispatchEvent(new Event('change'));
let txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLForSelect1);
selectElement.value="0";
selectElement.dispatchEvent(new Event('change'));
txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTML);
selectElement = document.getElementById("CSVDatasSelector1") as HTMLInputElement;
selectElement.value="1";
selectElement.dispatchEvent(new Event('change'));
txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTMLForSelect2);
selectElement.value="0";
selectElement.dispatchEvent(new Event('change'));
txtDatasViewsElt=document.getElementById("datas").innerHTML;
expect(txtDatasViewsElt).toEqual(fixtures.datasHTML);
});
});
});