MinnaHTML.js - A HTML Web Framework For Node.js
Saturday, 14th July 2012, 22:53
I finally made a public release of MinnaHTML.js on Github, if you know me and are a web developer, you have probably heard me go on about it a bit. If you know me and aren't, the name of it may not shock you, especially if you were familiar with my World of Warcraft mods, or my dog Minna. ;)
But First a Quick History
You can always skip to TL;DR at this point if you want.
So, a very long time ago now, I started out making websites with ASP/JScript, which was a very source heavy way of producing things. Every single webpage you accessed was basically the following:
<!-- #include virtual = "/_JavaScript/incMain.asp" -->
<%
// Some code to open a connection to the database
%><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
<meta name="generator" content="Hand Written By Elves">
<title>Management Links Home Page</title>
</head>
<body>
<table border="0">
<tr>
<td>
Some Heading
</td>
</tr>
<%
// A load of code at worse, or a function call at best, to insert some table rows
%>
</table>
</body>
</html>
<%
// Some code to close a connection to the database
%>
Over time, that became a horrendous insane messy way to spread code across multiple files, and generally make maintenance of the look a nightmare. So I progressed towards a more modular design, which was roughly like this:
<!-- #include virtual = "/_JavaScript/incMain.asp" -->
<%
// Some code to open a connection to the database
%>
<!-- #include virtual = "/_Styles/incPageHeader.asp" -->
<%
// Add anything dynamic to the header
%>
<!-- #include virtual = "/_Styles/incPageTop.asp" -->
<%
// Add some dynamic content
%>
<!-- #include virtual = "/_Styles/incPageBottom.asp" -->
<%
// Some code to close a connection to the database
%>
Now, that made it easier to change the look of the site within reason, but like every project that lives for a few years, it becomes unwieldy. And all of the above is a terrible way to write a web application, but back then who knew any better. You could say, a lot of PHP apps still don't, this is the land they also occupy.
Scripts are all very nice and that, if you just want to include a small bit of dynamic content on a page, but modern websites just are not built like that. They are in essence a full blown application, with the minor difference that where as the interface or UI to something like... say Microsoft Word, is delivered to you through the Windows API, web applications interface via HTML/CSS/Javascript and a web browser of your choice.
PHP and ASP Classic are just dreadful for developing webapps in, the former because of its horrendous language structure and design, and both for the scripting aspects. I'm not moaning about speed here, after all Perl is a very fast scripting language, but like PHP/ASP it is for scripting, and scripts are best suited to single tasks.
If you want to update some data every hour, Perl is a great choice, nodoby would think the same if you wanted to make a spreadsheet with Perl scripts. Wrong tool for the job.
So, anyway, I've been trying to move over the years towards a proper web application based framework. Unfortunately, .NET didn't grab me, in fact it put me off early on when all of the large websites I'd written with ASP Classic just refused to run under .NET, despite it supposedly being a simple matter of fixing and changing a few minor things.
When you've spent an entire week tweaking something so it would just compile under ASP.NET, and it still refuses to whilst bombing out with a meaningless error which even google can't help you work out but seems to be memory related, I feel being put off doing the whole thing is a valid state of mind.
Object Based Website Development
So the next port of call in my road to enlightment came a few years ago, when I went right back to my OOP C++ days and started rewriting everything (in Javascript of course) as objects. The webpage itself would be an object, you could add HTML to it as you went, and when you were done it would write itself out to the Response object.
Users were an object, the database was an object, forms were, lists were, almost everything all split up into seperate parts. The general idea was, if at some point down the road I wanted to output everything to something other than HTML, all I'd need to do was rewrite the objects.
My ASP classic pages now looked like this:
<!-- #include virtual = "/_Javascript/incGroupMain.asp" -->
<%
dbCreateConnection(true);
var objHtmlPage;
objHtmlPage = new PageObject();
contentBoxMainHeader();
contentBoxAddLogin();
contentBoxNavigation();
contentBoxPromos();
contentBoxArticle();
objHtmlPage.Write();
dbCloseConnection();
%>
And filled with code like this:
function contentBoxArticlesByAuthorSmall(objPresent)
{
var strList, objVBArray;
objHtmlPage.AddTabbedBoxHeader("More " + objPresent.strTemplateType + "s", "shadedbody");
// intuserindex, struserdisplayname, intcontentindex, strcontenttitle, strcontent, intcontentrefindex, strparentcontenttitle, strparentcontent, dtcreationdate
objVBArray = dbQueryGetRecentPromoContentTypeMatchingTagAndUser(objPresent.intTemplateTagIndex, objPresent.intContentType, objPresent.intUserIndex, 6);
var objHtmlList;
objHtmlList = new ListObject(false, "shortlist");
for (var i = 0; i <= objVBArray.ubound(2); i++)
{
objLink = new LinkObject(objPageLinkObject.GetUrlWithArticleAndSubdomain(objVBArray.getItem(2, i), "www", "/default.asp", strTitle), strPreviewImage, "title=\"" + strTitle + "\"");
objHtmlList.AddListItem(objLink);
}
objHtmlPage.AddHtml(objHtmlList.toString());
objHtmlPage.AddTabbedBoxFooter();
}
Still not there, still quite messy, and even more annoyingly, still requiring a page per specific URL type. But sometimes you need to play with things to see their limitations, and when Node.JS entered my life it came at the perfect time.
I knew from all my experiments over the years in producing webpages that the following things were important issues:
- Webpage content is often not generated in the order it is sent to the client
- Having a webpage represented as objects was useful
- Parts of webpages are a bit like quantum particles, sometimes they are best as objects, and other times best as text
TL;DR
Enter MinnaHTML.js, a simple, practical and easy to use library, which represents one half of the webapp puzzle. With it you can create webpages with as much, or as little object usage as you like. And it even comes with functions aimed at asynchronous development.
Now my webpages are built like this:
var mh = require("minnahtml");
var page = new mh.Html();
var head = new mh.Head(page);
var body = new mh.Body(page);
// Add style sheet, favicon, charset and jquery
new mh.Link(head, null, { rel: "shortcut icon", type: "image/ico", href: "/favicon.ico" } );
new mh.Meta(head, null, { "http-equiv": "content-type", content: "text/html", charset: "utf-8" } );
new mh.StyleSheet(head).href = "/styles/standard.css";
new mh.Script(head).src = "/scripts/jquery172.js";
new mh.Script(head).src = "/scripts/general.js";
// We want to wrap our main content in a div
new mh.Div(body, "mainarea");
// Add our standard header
new mh.Div(body, "header");
new mh.Div(body.header, "mainlogo");
// Add our standard footer
new mh.Div(body, "footer");
new mh.Div(body, "credits");
// Create our main content div
new mh.Div(body, "maincontent");
// Add our standard sidebar menu to the main content div
new mh.Div(body, "sidemenu");
And rendered like so:
page.whenReady(function(html) {
response.writeHead(wsWebsite.status, { "Content-Type": "text/html" });
response.end(html);
});
Because the objects are async aware, they can hold up page generation until they have been filled with data pulled from the database. They get filled with natural defaults, all follow an OOP structure with inheritance from related parents, and even have functions like nextObject()/eachChild()/findObject() for exploring/manipulating the page like you would the client side DOM.
Combined with a related OOP way of serving webpages, where parent objects contain generic aspects of the page like headers, footers, and so on, it's very easy to pass off a Div object to a function that has to fill it with a bunch of news, or a twitter feed for example, and no longer have to worry about it.
You can add things to the <head> section at anytime, which is handy if some pages require a load of admin related javascript and css, but you only want to include it when an admin is logged in.
The downside of this method is it requires you to understand HTML, and be the sort of person who builds your webpages using a text editor rather than Dreamwaver (is that still going? I have no idea). Small limit on the requirements I feel.