For this simple app, I'll use five functions. They are: Add error handler, get the data from the form, add the data to the table, sort table on due date, show any error to user.
Introduction
This is a continuation of the topics covered in my other two articles here:
- Decorator Pattern in VB.NET WinForms
- Reverse Decorator in VB.NET WinForms
The code in this article is available on GitHub here.
Implementation of what I call the Reverse Decorator. It is really just a take on the chain of responsibility pattern. It is a modular programming style that I believe follows the SOLID principles. I've used it in a couple of projects, all internal to my organization. I'd like to get some feedback on what others think. I'll stick with a simple To Do application as to focus most on the pattern. I'm going to assume that you have read and understand my other two articles and then this is just an implementation in JavaScript.
Background
I have been playing around with the decorator pattern for some time. I implemented it in VB.NET because that is the language I use most at work. I wrote my first CodeProject article with a decorator pattern, with explanations. I wrote the Reverse Decorator article next, also with explanations. I wanted to do this pattern in JavaScript because the language offers some advantages. The function doesn't have to be written in a class, more flexibility on the dataObj
, and we don't need to implement an interface. I'm not going to cover the ground I covered in the other two articles mainly because I just wanted to publish this example.
Starting Point
We are starting with a todo.html file. In the HTML, I've created the basic UI for our To Do app. It references Bootstrap CSS. Nothing too exciting. We will be starting with two JavaScript files to help us organize our project. The "todo.js" file is where we will compose our action and then run it. In the "todoAdd.js", we will define all the functions. I find it easier for maintenance to separate the functions from the composition and running.
Functions First
We'll start with the functions file "todoAdd.js". The basic functions will have this format:
function functionName(anyPramsHere, next){
return function(dataObj){
if(next){
next(dataObj);
}
};
}
This format will allow us to compose the functions together. One benefit of using JavaScript over VB.NET is we don't have to declare a class. We can just use a function that can return another function. "next
" is the following function, dataObj
is the class that passes the state from one function to the next.
For this simple app, I'll use five functions. They are:
- Add error handler
- Get the data from the form
- Add the data to the table
- Sort table on due date
- Show any error to user
None of these functions are particularly earth shattering, so we'll have them done, like this:
function getDataFromForm(next){
return function(dataObj){
if(!dataObj.hasError){
dataObj.dtAddDueDate = document.getElementById("dtAddDueDate").value;
dataObj.txtAddTask = document.getElementById("txtAddTask").value;
}
if(next){
next(dataObj);
}
};
}
function addToTable(next){
return function(dataObj){
if(!dataObj.hasError){
var newRow = document.createElement("tr");
var ckCol = document.createElement("td");
var ck = document.createElement("input");
ck.type = "checkbox";
ckCol.appendChild(ck)
newRow.appendChild(ckCol);
var dtCol = document.createElement("td");
dtCol.innerText = dataObj.dtAddDueDate;
newRow.appendChild(dtCol);
var taskCol = document.createElement("td");
taskCol.innerText = dataObj.txtAddTask;
newRow.appendChild(taskCol);
var tblBody = document.getElementById("tblBody");
tblBody.appendChild(newRow);
}
if(next){
next(dataObj);
}
};
}
function sortTableOnDueDate(next){
return function(dataObj){
var rows = document.getElementById("tblBody").rows;
var numSorted;
do {
numSorted = 0;
for (i = 0; i < rows.length; i++) {
var currEl = rows[i];
var nextEl = currEl.nextElementSibling;
if ((nextEl) &&
(new Date(currEl.cells[1].innerText) > new Date(nextEl.cells[1].innerText))) {
document.getElementById("tblBody").insertBefore(nextEl, currEl);
numSorted++;
}
}
} while (numSorted > 0);
if(next){
next(dataObj);
}
};
}
function addErrHandler(next) {
return function (dataObj) {
dataObj.err = {
hasError: false,
message: ""
}
try {
if (next) {
next(dataObj);
}
} catch (err) {
dataObj.err.hasError = true;
dataObj.err.message = err.message;
next(dataObj);
}
};
}
function showErrorToUser(next){
return function(dataObj){
if(dataObj.err.hasError){
alert("ERROR: " + dataObj.err.message);
}
if(next){
next(dataObj);
}
};
}
Compose the Functions Together
In the todo.js file, we will put the functions together. We compose them backwards so when they run, they will run forward. We do it like this:
var addToDo = function(){
var runMe = null;
runMe = showErrorToUser(runMe);
runMe = sortTableOnDueDate(runMe);
runMe = addToTable(runMe);
runMe = getDataFromForm(runMe);
runMe = addErrHandler(runMe);
dataObj = {};
runMe(dataObj);
};
Composing backward and running forward allows us to use async
/await
patterns. If you are wondering why, see my VB.NET article on the Reverse Decorator.
At the end of the composition, we create the dataObj
. It is where we will persist state as we go from function to function.
Run it and it works!
History
- 1st December, 2018: Initial version
- 3rd December, 2018: Article updated