Dynamic Editable HTML Table

INTRO

This example will allow you to:

Features:

SETUP

You can simply download the demo aia project provided, and open this up in AI2.

Or, copy the provided html file (and image file if you want the "metricrat" icons ;) ), and upload these to your assets. Then (accurately) copy the blocks from the blocks image.

All the files you need are provided below.

HTML

(there is a lot of it, 329 lines....)

<!DOCTYPE html>

<html>

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">

<head>

<title>Dynamic Editable Table</title>

<style>

button {width:30%}

.w3-container {padding-top:10px}

#display {overflow: auto;height: 80vh;}

th[scope="row"], thead tr {position:sticky;top:0;}

th, td {white-space: nowrap;}

</style>

</head>

<body>

<div class="w3-container">

<div id="delmod" class="w3-modal">

<div class="w3-modal-content w3-card-4 w3-round">

<div class="w3-center"><br>

<p>Are you sure you want<br>to delete this record ?</p>

</div>

<div class="w3-bar w3-center">

<button onclick="modResponse('Yes');" type="button" style="width:25%;" class="w3-btn w3-green w3-round w3-small">Yes</button>

<button onclick="modResponse('No');"  type="button" style="width:25%;" class="w3-btn w3-red w3-round w3-small">Cancel</button>

</div>

<br>

</div>

</div>

</div>

</div>

<div class = "w3-container w3-bar w3-center">

<button id = "btEdit" class="w3-btn w3-small w3-blue w3-round" onclick="Edit()" disabled>Edit</button>

<button id = "btAdd"  class="w3-btn w3-small w3-yellow w3-round" onclick="Add()" disabled>Add</button>

<button id = "btSelect"  class="w3-btn w3-small w3-purple w3-round" onclick="Select()" disabled>Select</button>


</div>

<div class = "w3-container w3-bar w3-center">

<button id = "btDelete"  class="w3-btn w3-small w3-red w3-round" onclick="Delete()" disabled>Delete</button>

<button id = "btSave"  class="w3-btn w3-small w3-green w3-round" onclick="Save()" disabled>Save</button>

<button id = "btCancel"  class="w3-btn w3-small w3-orange w3-round" onclick="Cancel()" disabled>Cancel</button>

</div>

<div class = "w3-container">

<div id="display" class = "w3-responsive">Loading table ...</div>

</div>

<br>


<script src = "./tableDemoData.js"></script>


<script>


document.getElementById("btEdit").disabled = false;

document.getElementById("btAdd").disabled = false;

document.getElementById("btSelect").disabled = false;


var tableData;

var colLength = "";

var rowSelect = "off";

var rec;

var SelectedRow = "";


if (window.AppInventor) {

var arr = JSON.parse(window.AppInventor.getWebViewString());

tableData = convertArrayToJson(arr);

} else {

if (sessionStorage.Table) {

tableData = JSON.parse(sessionStorage.Table);

} else {

tableData = tableDemoData;

}

}


const createHead = (headObject) => {

    let cells = "";

    for (const key in headObject) {

      cells += `<th id = ${key}>${key}</th>`; 

    }

    return `<tr class = 'w3-green'>${cells}</tr>`;

};


const createRow = (rowObject) => {

    let cells = "";

    for (const key in rowObject) {

      cells += `<td>${rowObject[key]}</td>`;

    }

    return `<tr onclick="highlight(this);">${cells}</tr>`;

};


const createTable = (tableData) => {

    const rows = tableData.map(d => {

      return createRow(d)

    });

    const headRow = createHead(tableData[0])

    colLength = (headRow.match(/<th id/g) || []).length;

    let optimizedRows = '';

    rows.forEach((row) => {

      optimizedRows += row;

    });

    const table = (`

      <table id = "det" class = "w3-table-all w3-card-4">

        <thead>${headRow}</thead>

        <tbody>${optimizedRows}</tbody>

      </div>

    `);

    document.getElementById("display").innerHTML = table;

};


createTable(tableData);


function Edit() {

document.getElementById("btCancel").disabled = false;

document.getElementById("btSave").disabled = false;

document.getElementById("btSelect").disabled = true;

document.getElementById("btAdd").disabled = true;

document.getElementById("btEdit").disabled = true;

const element = document.getElementById("display");

const nodes = element.getElementsByTagName("td");

if (testArrayUID('det','id')) {

for (let i = 0; i < nodes.length; i++) {

if (i != 0 && i%colLength != 0) {

nodes[i].contentEditable = "true";

}

}

} else {

for (let i = 0; i < nodes.length; i++) {

nodes[i].contentEditable = "true";

}

}


};


function Add() {

document.getElementById("btCancel").disabled = false;

document.getElementById("btSave").disabled = false;

document.getElementById("btEdit").disabled = true;

document.getElementById("btSelect").disabled = true;

var maxId = getIdArray("det","id");

var det = tableToArray('det');

var addArr = [];

if (maxId == null) {

addArr.push(maxId);

} else {

addArr.push(maxId+1);

}

for (i=1;i<colLength;i++) {

addArr.push(null);

    }

det.push(addArr);

    document.getElementById("det").remove();

createTable(convertArrayToJson(det));

};


function Select() {

if (rowSelect == "off") {

rowSelect = "on";

document.getElementById("btSelect").innerHTML = "DeSelect";

document.getElementById("btAdd").disabled = true;

document.getElementById("btEdit").disabled = true;


} else {

rowSelect = "off"

document.getElementById("btSelect").innerHTML = "Select";

document.getElementById("btAdd").disabled = false;

document.getElementById("btEdit").disabled = false;

document.getElementById("btDelete").disabled = true;

deHighlight();

}

};


function Delete() {

rec = getSelectedRowValue();

document.getElementById('delmod').style.display='block';

};


function Save() {

document.getElementById("btCancel").disabled = true;

document.getElementById("btSave").disabled = true;

document.getElementById("btSelect").disabled = false;

document.getElementById("btAdd").disabled = false;

document.getElementById("btEdit").disabled = false;

const element = document.getElementById("display");

const nodes = element.getElementsByTagName("td");

for (let i = 0; i < nodes.length; i++) {

nodes[i].contentEditable = "false";

}

if (window.AppInventor) {

var det = tableToArray('det');

window.AppInventor.setWebViewString(JSON.stringify(det));

document.getElementById("det").remove();

createTable(convertArrayToJson(det));

} else {

var det = tableToArray('det');

var jsonObject = convertArrayToJson(det);

sessionStorage.Table = JSON.stringify(jsonObject);

document.getElementById("det").remove();

createTable(jsonObject);

}

};


function Cancel() {

document.getElementById("btCancel").disabled = true;

document.getElementById("btSave").disabled = true;

document.getElementById("btSelect").disabled = false;

document.getElementById("btAdd").disabled = false;

document.getElementById("btEdit").disabled = false;

document.getElementById("det").remove();

createTable(tableData);


};


function tableToArray(tableId) {

myData = document.getElementById(tableId).rows

myList = []

    for (var i = 0; i < myData.length; i++) {

    el = myData[i].children

myEl = []

        for (var j = 0; j < el.length; j++) {

        myEl.push(el[j].innerText);

        }

        myList.push(myEl)

    }

return myList

};


function getIdArray(tableId,colId) { 

    var myTab = document.getElementById(tableId); 

    var index = document.getElementById(colId).cellIndex; 

    var maxId = []; 

    for (i = 1; i < myTab.rows.length; i++) { 

        var objCells = myTab.rows.item(i).cells; 

        for (var j = index; j <= index; j++) { 

            maxId.push(objCells.item(j).innerHTML);

        } 

    }

    if( Math.max(...maxId) == NaN) {

         return null 

    } else { 

return Math.max(...maxId)

}

};


function convertArrayToJson(theArray) {

const [keys, ...values] = theArray;

    const JsonObject = values.map(array => array.reduce((a, v, i) => ({...a, [keys[i]]: v}), {}));

    return JsonObject;

}


function highlight(row) {

if (rowSelect == "on") {

document.getElementById("btDelete").disabled = false;

SelectedRow = row.cells[0].textContent;

deHighlight();

row.style.backgroundColor = 'palegreen';

row.classList.toggle("selectedRow");

}

}


function deHighlight() { 

    let table = document.getElementById("det");

    let rows = table.rows;

    for (let i = 0; i < rows.length; i++) {

if ( i%2 == 0 ) {

rows[i].style.backgroundColor = "#f1f1f1";

} else { 

        rows[i].style.backgroundColor = "transparent";

}

    }   

}


function getSelectedRowValue() {

deHighlight();

    return SelectedRow;

}


function modResponse(resp) {

document.getElementById('delmod').style.display='none';

if (resp == 'Yes') {

var det = tableToArray('det');

for (var i = 0; i < det.length; i++) {

if (det[i][0] == rec) {

det.splice(i,1);

createTable(convertArrayToJson(det));

}

}

if (window.AppInventor) {

window.AppInventor.setWebViewString(JSON.stringify(det));

} else {

sessionStorage.Table = JSON.stringify(convertArrayToJson(det));

}

}

 

Select();


};


function testArrayUID(tableId,colId) { 

     var myTab = document.getElementById(tableId); 

     var index = document.getElementById(colId).cellIndex; 

     var colVals = []; 

     for (i = 1; i < myTab.rows.length; i++) { 

         var objCells = myTab.rows.item(i).cells; 

         for (var j = index; j <= index; j++) { 

             colVals.push(objCells.item(j).innerHTML);

         } 

}

         var test;

         for (var i=0;i<colVals.length;i++) {

if (Number.isInteger(parseInt(colVals[i]))){

test = true;

} else {

test = false;

break

}

}

     if(tableData.length == new Set(colVals).size && test == true) {

return true;

} else {

return false;

};


</script>


</body>

</html>


BLOCKS

VIDEO

to follow....

RESOURCES

Stack Overflow - thanks to all the great people for their solutions to html, javascript and css ideas, answers and solutions

W3Schools - for the w3.css framework, and for the vast html, javascript and css resource

Other internet sources too numerous to mention individually

Link to topic on the AI2 community where you can ask questions, make comment....