While building my Todo page, I wanted to see if It’d be possible to parse the todo.txt spec and then render a table from it.
However, rendering anything in the DOM from plain JS can get super bulk and messy really quickly, so I thought I’d write a cleaner, reusable utility to render out tables.
I write all my scripts in Typescript, so first lets layout some basic data & types.
We’ve got a Column generic Type that contains information about our columns.
export interface Column<T> {
: string;
header: keyof T;
accessor?: (value: T[keyof T], row?: T) => string;
renderer }
And then we have a basic footprint of our render function.
export const renderTable = <T>(args: {
: Column<T>[];
columns: T[];
data: Element => {} })
We’re going to pass in an array of Objects, with the object type here being T
.
This function will be used accordingly:
import {renderTable} from "./render-table";
interface ExampleDataInterface {
: string;
name: number;
age
}
const exampleData: ExampleDataInterface = [
: "John Doe", age: 25, approved: false},
{name: "Jane Doe", age: 26, approved: true}
{name
]
const tableElement = renderTable<ExampleDataInterface>({
: [
columns: 'Legal Name', accessor: 'name'},
{header: 'Users Age', accessor: 'age'},
{header
{: 'Approved',
header: 'approved',
accessor: (value) => value ? 'Yes' : 'No'
renderer
},
]: exampleData
data })
Inside our render function we first want to destructure our arguments and create our basic elements.
// Destructure our args into their own variables
const { columns, data, sortable } = args;
// Create the base table elements
const wrapper = document.createElement('div');
const table = document.createElement('table');
const header = document.createElement('thead');
const body = document.createElement('tbody');
// Connect the base table elements together
.append(header);
table.append(body);
table.append(table); wrapper
We now want to generate our header row where our Column titles will live.
// Create the header row elements and apply a className
const headerRow = document.createElement('tr');
.className = 'header';
headerRow
// For our columns, add a th containing the header value
.forEach((cell) => {
columnsconst columnHeader = document.createElement('th');
.innerText = cell.header;
columnHeader.append(columnHeader);
headerRow;
})
// Append the header tr to the tables thead
.append(headerRow); header
Next we want to generate our data rows. We can do this by iterating over each item in our data
array and then iterating over each column
.forEach((cell) => {
data// Create a new row for this data
const row = document.createElement('tr');
// For each column in the table
.forEach((parentColumn) => {
columns// Create a new cell
const newCell = document.createElement('td');
// Set the cells inner text to either the output of the provided renderer, or the raw text
.innerText = `${
newCell.renderer
parentColumn? parentColumn.renderer(cell[parentColumn.accessor], cell)
: cell[parentColumn.accessor]
}`;
// Append the cell to the newly created row
.append(newCell);
row;
})
// Append the row to the tables tbody
.append(row);
body; })