095 — Rendering an HTML Template (ListTemplate render with HasFormatter items)
Full companion narrative for TypeScript Tutorial #17 — the ListTemplate class: ul container via parameter property, render with a HasFormatter item, literal-union position, and wiring the submit handler
Video: 095 · TypeScript Tutorial #17 - Rendering an HTML Template · watch · ~10:09 · Net Ninja TypeScript tutorial series, lesson 17 (Shaun)
Source: this is a written companion to the video. Part of the Favorite TypeScript Videos curated path, module 05 (objects and interfaces). Closes the loop from 094: the form built
Invoice/Paymentobjects but only logged them — now they render into the page.
the plan, as comments
The lesson opens with the whole class sketched as a comment block before a line of implementation exists — register a list container in the constructor, then a render method that takes an invoice-or-payment plus a heading plus a position, builds the li/h4/p template, and inserts it at the start or end:
/**
* register a list container (ul) in the constructor
* create a render method to render a new 'li' to the container
* -- accepts arguments: invoice or payment, a heading, a position
* -- create the html template (li, h4, p)
* -- add the 'li' template to the start/end of the list
*/

the destination first: wired app.ts
Before building the class, the finished app.ts wiring is shown so every later step has a target: grab the single ul (querySelector('ul')! — the ! because querySelector can return null), construct one ListTemplate, and in the submit handler build a HasFormatter-typed doc (invoice or payment branch) then list.render(doc, type.value, 'end'):
// List template instance
const ul = document.querySelector("ul")!;
const list = new ListTemplate(ul);
form.addEventListener("submit", (e: Event) => {
e.preventDefault();
let values: [string, string, number];
values = [tofrom.value, details.value, amount.valueAsNumber];
let doc: HasFormatter;
if (type.value === "invoice") {
doc = new Invoice(...values);
} else {
doc = new Payment(...values);
}
list.render(doc, type.value, "end");
});
Note the tuple-typed values plus spread (...values) — lesson 20’s payoff previewed three lessons early. Old console.log lines are deleted: the format() string moves from console to page.

the class shell and constructor
src/classes/ListTemplate.ts starts as a shell carrying the plan comment, then gains the constructor — a parameter property (private container: HTMLUListElement) that registers the ul with no field declaration, the module-05 shorthand from 013:
export class ListTemplate {
constructor(private container: HTMLUListElement) {}
}


render, built line by line
The signature carries three module-05 patterns at once: item: HasFormatter so either class qualifies and item.format() is guaranteed, heading: string for the h4, and pos: 'start' | 'end' — a literal union, so only those two strings compile:
render(item: HasFormatter, heading: string, pos: 'start' | 'end') {
const li = document.createElement('li');
}
“Now, what type is this gonna be? Well, it’s not gonna be invoice cos it could be a payment as well, and it’s not gonna be payment because it could be an invoice as well. But we could use this thing right here, this interface, has formatter and say that look, whatever you pass in as the first item right here, that object has to be has formatter, or implement the has format interface.”

Then the template body — plain DOM API (createElement, innerText, append/prepend) with types inferred from the tag string (li hovers as HTMLLIElement, no annotations):
const h4 = document.createElement("h4");
h4.innerText = heading;
li.append(h4);
const p = document.createElement("p");
p.innerText = item.format();
li.append(p);
if (pos === "start") {
this.container.prepend(li);
} else {
this.container.append(li);
}
“So then, the first thing we wanna do right here is create the LI, so I’m gonna say const LI is equal to document.re element. This is just regular JavaScript right here, and we’re creating an LI.”


wiring the handler and verifying in the browser
Back in app.ts: the ul query, one ListTemplate instance, the doc branch from 094, and the single new line — list.render(doc, type.value, 'end'). Payoff: submitting an invoice for mario appends <li><h4>invoice</h4><p>mario owes £250 for website work</p></li> to the rendered list.



Prev: 094 — Interfaces with Classes · Next: 096 — Generics
The auto-transcript hears “URL” wherever the instructor says ul; “Finance Logger” and “TypeScript Logger” in a few segments both mean the ListTemplate class/instance, and “Add The Net Ninja” is the form’s Add button, not a TypeScript term. One frame OCR shows HasFormatter imported without the .js specifier — the 014 rule (write .js, the browser loads compiled output) still applies.