Ajout d'un moteur de rendu alternatif pour des affichages de données plus complexes.
This commit is contained in:
parent
3d6c973ec4
commit
2f3f5df255
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "freedatas2html",
|
||||
"version": "1.2.1",
|
||||
"version": "1.3.0",
|
||||
"description": "Conversion and display of data in different formats (CSV, JSON, HTML) with the possibility of filtering, classifying and paginating the results.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@ -48,4 +48,4 @@
|
||||
"natural-orderby": "^2.0.3",
|
||||
"papaparse": "^5.3.1"
|
||||
}
|
||||
}
|
||||
}
|
68
src/build/extensions/MixedFieldsRender.js
Normal file
68
src/build/extensions/MixedFieldsRender.js
Normal file
@ -0,0 +1,68 @@
|
||||
var errors = require("../errors.js");
|
||||
var MixedFieldsRender = (function () {
|
||||
function MixedFieldsRender(settings) {
|
||||
this._fields = [];
|
||||
this.fieldRenders = [];
|
||||
this.datas = [];
|
||||
this.settings = settings;
|
||||
}
|
||||
Object.defineProperty(MixedFieldsRender.prototype, "fields", {
|
||||
get: function () {
|
||||
return this._fields;
|
||||
},
|
||||
set: function (fields) {
|
||||
if (fields.length === 0)
|
||||
throw new Error(errors.renderNeedFields);
|
||||
else
|
||||
this._fields = fields;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
MixedFieldsRender.prototype.rend2HTML = function () {
|
||||
if (this._fields.length === 0)
|
||||
throw new Error(errors.renderNeedFields);
|
||||
else {
|
||||
var datasHTML = this.settings.allBegining;
|
||||
if (this.settings.fieldsNamesDisplaying !== undefined) {
|
||||
var fieldsNamesRend = this.settings.fieldsNamesDisplaying;
|
||||
for (var i = 0; i < this._fields.length; i++)
|
||||
fieldsNamesRend = fieldsNamesRend.replace("##" + i + "##", this._fields[i]);
|
||||
datasHTML += fieldsNamesRend;
|
||||
}
|
||||
if (this.settings.linesBegining !== undefined)
|
||||
datasHTML += this.settings.linesBegining;
|
||||
for (var _i = 0, _a = this.datas; _i < _a.length; _i++) {
|
||||
var row = _a[_i];
|
||||
var lineRend = this.settings.datasLinesDisplaying;
|
||||
var _loop_1 = function (i) {
|
||||
var currentField = this_1._fields[i];
|
||||
if (row[currentField] !== undefined) {
|
||||
var rowRend = row[currentField];
|
||||
var render = this_1.fieldRenders.find(function (item) {
|
||||
return item.name === currentField;
|
||||
});
|
||||
if (render !== undefined)
|
||||
rowRend = render.rend2HTML(row);
|
||||
lineRend = lineRend.replace(new RegExp("##" + i + "##", "g"), rowRend);
|
||||
}
|
||||
else
|
||||
lineRend = lineRend.replace(new RegExp("##" + i + "##", "g"), "");
|
||||
};
|
||||
var this_1 = this;
|
||||
for (var i = 0; i < this._fields.length; i++) {
|
||||
_loop_1(i);
|
||||
}
|
||||
datasHTML += lineRend;
|
||||
}
|
||||
if (this.settings.linesEnding !== undefined)
|
||||
datasHTML += this.settings.linesEnding;
|
||||
datasHTML += this.settings.allEnding;
|
||||
return datasHTML;
|
||||
}
|
||||
};
|
||||
return MixedFieldsRender;
|
||||
}());
|
||||
export { MixedFieldsRender };
|
||||
export { FreeDatas2HTML } from "../FreeDatas2HTML";
|
||||
export { errors };
|
109
src/extensions/MixedFieldsRender.ts
Normal file
109
src/extensions/MixedFieldsRender.ts
Normal file
@ -0,0 +1,109 @@
|
||||
// Alternative à Rendern pour des intégrations plus complexes.
|
||||
// Le contenu des divers champs peut ici être mixé à l'affichage ou encore affiché plusieurs fois...
|
||||
|
||||
import { DatasRenders } from "../interfaces";
|
||||
const errors=require("../errors.js");
|
||||
|
||||
interface DatasRendersSettings
|
||||
{
|
||||
allBegining: string;
|
||||
allEnding: string;
|
||||
linesBegining?: string,
|
||||
linesEnding?: string,
|
||||
fieldsNamesDisplaying?: string; // ##n## sera remplacé par le nom du nième champ, etc.
|
||||
datasLinesDisplaying: string;// ##n## sera remplacé par la valeur du nième champ, etc.
|
||||
}
|
||||
interface FieldRender
|
||||
{
|
||||
name: string; // nom du champ concerné.
|
||||
rend2HTML(lineValues: {[index: string]:string} ) : string; // fonction spécifique appelée avant d'injecter la valeur de ce champ dans datasLinesDisplaying
|
||||
}
|
||||
|
||||
export class MixedFieldsRender implements DatasRenders
|
||||
{
|
||||
private _fields: string[]=[];
|
||||
public settings: DatasRendersSettings;
|
||||
public fieldRenders: FieldRender[]=[];
|
||||
public datas: {[index: string]:string}[]=[];
|
||||
|
||||
constructor(settings: DatasRendersSettings)
|
||||
{
|
||||
this.settings=settings;
|
||||
}
|
||||
|
||||
// Les données fournies au Render peuvent être vides du fait de l'action des filtres ou encore de la pagination...
|
||||
// Mais les noms des champ doivent être fournis.
|
||||
set fields(fields: string[])
|
||||
{
|
||||
if(fields.length === 0)
|
||||
throw new Error(errors.renderNeedFields);
|
||||
else
|
||||
this._fields=fields;
|
||||
}
|
||||
|
||||
get fields() : string[]
|
||||
{
|
||||
return this._fields;
|
||||
}
|
||||
|
||||
public rend2HTML() : string
|
||||
{
|
||||
if(this._fields.length === 0)
|
||||
throw new Error(errors.renderNeedFields);
|
||||
else
|
||||
{
|
||||
let datasHTML=this.settings.allBegining;
|
||||
|
||||
// Création d'une éventuelle entête :
|
||||
if(this.settings.fieldsNamesDisplaying !== undefined)
|
||||
{
|
||||
// Dans le cas de champs mixés, tous les champs utilisés ne sont pas forcément listés en entête.
|
||||
// Il suffit alors qu'ils soient absent de fieldsNamesDisplaying.
|
||||
let fieldsNamesRend=this.settings.fieldsNamesDisplaying;
|
||||
for(let i=0; i< this._fields.length; i++)
|
||||
fieldsNamesRend=fieldsNamesRend.replace("##"+i+"##", this._fields[i]);
|
||||
datasHTML+=fieldsNamesRend;
|
||||
}
|
||||
|
||||
if(this.settings.linesBegining !== undefined)
|
||||
datasHTML+=this.settings.linesBegining;
|
||||
|
||||
// Suivant les objets/lignes, les champs peuvent se trouver dans un ordre différent.
|
||||
// Ou encore certains peuvent manquer, être en trop...
|
||||
// Mais tous les champs présents dans "fields" doivent être traités en respectant leur ordre pour être cohérent avec l'éventuelle entête créée ci-dessus.
|
||||
for(let row of this.datas)
|
||||
{
|
||||
// Idem que l'entête : tous les champs ne sont pas forcément présents dans datasLinesDisplaying.
|
||||
let lineRend=this.settings.datasLinesDisplaying;
|
||||
for(let i=0; i< this._fields.length; i++)
|
||||
{
|
||||
const currentField=this._fields[i];
|
||||
if(row[currentField] !== undefined) // champ renseigné pour cet enregistrement
|
||||
{
|
||||
// Par défaut, l'emplacement dans datasLinesDisplaying sera remplacé par la valeur renseignée :
|
||||
let rowRend=row[currentField];
|
||||
// Sauf si une fonction de pré-traitement a été fournie pour ce champ :
|
||||
const render = this.fieldRenders.find(function(item)
|
||||
{
|
||||
return item.name === currentField;
|
||||
});
|
||||
if(render !== undefined)
|
||||
rowRend=render.rend2HTML(row);
|
||||
lineRend=lineRend.replace(new RegExp("##"+i+"##", "g"), rowRend); // RegExp, car bug TS avec replaceAll (parce que target = es5 ?).
|
||||
}
|
||||
else // si ce champ n'est pas renseigné, il faut tout de même supprimer l'indicateur de son emplacement :
|
||||
lineRend=lineRend.replace(new RegExp("##"+i+"##", "g"), "");
|
||||
}
|
||||
datasHTML+=lineRend;
|
||||
}
|
||||
if(this.settings.linesEnding !== undefined)
|
||||
datasHTML+=this.settings.linesEnding;
|
||||
datasHTML+=this.settings.allEnding;
|
||||
return datasHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Utile au script de tests :
|
||||
export { FreeDatas2HTML } from "../FreeDatas2HTML";
|
||||
export { errors };
|
96
tests/extensions/mixedFieldsRenderSpec.ts
Normal file
96
tests/extensions/mixedFieldsRenderSpec.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { errors, FreeDatas2HTML, MixedFieldsRender } from "../../src/extensions/MixedFieldsRender";
|
||||
const fixtures=require("../fixtures.js");
|
||||
|
||||
describe("Test du moteur de rendu alternatif avec contenus mixables, etc.", () =>
|
||||
{
|
||||
let render: MixedFieldsRender;
|
||||
const fields=[ "Z", "Élément", "Symbole", "Famille" ] ;
|
||||
// Les champs des différentes lignes ne sont pas forcément dans le même ordre et certains peuvent être absents, vides ou encore en trop...
|
||||
const datas=[{"Z":"1","Élément":"Hydrogène","Symbole":"H","Famille":"Non-métal"},{"Famille":"Gaz noble","Élément":"Hélium","Z":"2","Symbole":"He"},{"Champ ignoré":"Je me champ ignoré !", "Z":"3","Élément":"Lithium","Famille":"Métal alcalin","Symbole":"Li"},{"Z":"4","Élément":"Béryllium","Famille":"","Champ ignoré":"Je me champ ignoré !"}] ;
|
||||
let renderSettings=
|
||||
{
|
||||
allBegining: "<div>",
|
||||
allEnding: "</div>",
|
||||
linesBegining: "<ul>",
|
||||
linesEnding: "</ul>",
|
||||
fieldsNamesDisplaying: "<h3>##0## | ##1## | ##3## | ##2##</h3>",
|
||||
datasLinesDisplaying: "<li>##0## | ##1## | ##3## | ##2##</li>"
|
||||
};
|
||||
|
||||
beforeEach(() =>
|
||||
{
|
||||
render=new MixedFieldsRender(renderSettings);
|
||||
});
|
||||
|
||||
it("Doit générer une erreur, si une liste de champs vide lui est fournie.", () =>
|
||||
{
|
||||
expect(() => { return render.fields=[]; } ).toThrowError(errors.renderNeedFields);
|
||||
expect(render.fields).toEqual([]);
|
||||
});
|
||||
|
||||
it("Doit accepter toute liste de champs valide.", () =>
|
||||
{
|
||||
render=new MixedFieldsRender(renderSettings);
|
||||
expect(() => { return render.fields=[""]; }).not.toThrowError(); // pas normal, mais pas testé par cette classe
|
||||
expect(render.fields).toEqual([""]);
|
||||
expect(() => { return render.fields=["je vois double", "je vois double"]; }).not.toThrowError(); // idem
|
||||
expect(render.fields).toEqual(["je vois double", "je vois double"]);
|
||||
expect(() => { return render.fields=["je me sens seul"]; }).not.toThrowError();
|
||||
expect(render.fields).toEqual(["je me sens seul"]);
|
||||
});
|
||||
|
||||
it("Doit générer une erreur, si lancé sans avoir fourni une liste des champs.", () =>
|
||||
{
|
||||
expect(() => { return render.rend2HTML(); }).toThrowError(errors.renderNeedFields);
|
||||
});
|
||||
|
||||
it("Ne doit pas générer d'erreur, si lancé avec une liste des champs, même s'il n'y a aucune donnée fournie.", () =>
|
||||
{
|
||||
render.fields=fields;
|
||||
expect(() => { return render.rend2HTML(); }).not.toThrowError();
|
||||
});
|
||||
|
||||
it("Doit retourner un code HTML correspondant à la configuration fournie, avec ou sans données à afficher.", () =>
|
||||
{
|
||||
render.fields=fields;
|
||||
let html=render.rend2HTML();
|
||||
expect(html).toEqual("<div><h3>Z | Élément | Famille | Symbole</h3><ul></ul></div>");
|
||||
// Avec des données :
|
||||
render.datas=datas;
|
||||
html=render.rend2HTML();
|
||||
expect(html).toEqual("<div><h3>Z | Élément | Famille | Symbole</h3><ul><li>1 | Hydrogène | Non-métal | H</li><li>2 | Hélium | Gaz noble | He</li><li>3 | Lithium | Métal alcalin | Li</li><li>4 | Béryllium | | </li></ul></div>");
|
||||
});
|
||||
|
||||
it("Doit retourner un code HTML prenant en compte les fonctions spécifiques d'affichage, ou encore les champs affichés plusieurs fois.", () =>
|
||||
{
|
||||
const newRenderSettings=
|
||||
{
|
||||
allBegining: "<div>",
|
||||
allEnding: "</div>",
|
||||
linesBegining: "<ul>",
|
||||
linesEnding: "</ul>",
|
||||
fieldsNamesDisplaying: "<h3>##0## | ##1## | ##3## | ##2##</h3>",
|
||||
datasLinesDisplaying: "<li>##0## | ##1## (##0##) | ##3## | ##2##</li>"
|
||||
};
|
||||
const newRender=new MixedFieldsRender(newRenderSettings);
|
||||
const rendFamille2HTML=(values: {[index: string]:string} ) : string =>
|
||||
{
|
||||
if(values["Famille"] !=="")
|
||||
return values["Famille"];
|
||||
else
|
||||
return "Non renseigné"; // doit apparaître car "Famille" est vide mais existant pour le Béryllium.
|
||||
};
|
||||
const rendSymbol2HTML=(values: {[index: string]:string} ) : string =>
|
||||
{
|
||||
if(values["Symbole"] !=="")
|
||||
return values["Symbole"];
|
||||
else
|
||||
return "Inconnu"; // ne doit pas apparaître, car "Symbole" n'existe pas du tout par le Béryllium, donc rendSymbol2HTML() ne doit pas être appelée.
|
||||
};
|
||||
newRender.fieldRenders=[{ name:"Famille", rend2HTML: rendFamille2HTML}, { name:"Symbole", rend2HTML: rendSymbol2HTML}];
|
||||
newRender.fields=fields;
|
||||
newRender.datas=datas;
|
||||
let html=newRender.rend2HTML();
|
||||
expect(html).toEqual("<div><h3>Z | Élément | Famille | Symbole</h3><ul><li>1 | Hydrogène (1) | Non-métal | H</li><li>2 | Hélium (2) | Gaz noble | He</li><li>3 | Lithium (3) | Métal alcalin | Li</li><li>4 | Béryllium (4) | Non renseigné | </li></ul></div>");
|
||||
});
|
||||
});
|
@ -8,6 +8,7 @@ module.exports =
|
||||
exampleWithCSV: "./src/demo/exampleWithCSV.ts",
|
||||
exampleWithHTML: "./src/demo/exampleWithHTML.ts",
|
||||
exampleWithJSON: "./src/demo/exampleWithJSON.ts",
|
||||
exampleWithMixedFields: "./src/demo/exampleWithMixedFields.ts",
|
||||
exampleWithUL: "./src/demo/exampleWithUL.ts",
|
||||
exampleWithUserFile: "./src/demo/exampleWithUserFile.ts"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user