Introduction to DML - part 2: using functional templating
Eckehard
Posted on August 8, 2021
DML features a new approach to create dynamic web content using Javascript only. This enables some new design patterns. This post will explain the use of Functional Templating.
Templates in other frameworks are design patterns generated from short HTML snippets. They may contain of one or more HTML-elements that are arranged during runtime using external parameters. As DML generates all content dynamically, templates can be provided by the use of functions or classes. This is called "Functional Templating". As a demonstration, a simple calculator application is build using template-functions. Each step is explained in detail.
Simple calculator app
You will need an empty HTML-page and two external libraries:
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<script src="https://efpage.de/DML/DML_homepage/lib/DML-min.js"></script>
A functional template may be as simple as follows:
// round button
function RB(s, color) {
const r = 30;
return button(s, "width:" + px(r) + "; height:" + px(r) + "; border-radius: 50%; background-color: " + color + "; margin: 3px;")
}
This creates a round button with a diameter of 30 pixels. This is just a button, not a real template. But it may serve as a demonstrations, that different properties (and elements) can be combined using functions, that act as a template.
For our application, we need two types of buttons
- yellow buttons for the numbers
- gray buttons for the operators
So, we add two more functions that use RB() and add some more properties:
// yellow button for numbers
function number(s) {
let bt = RB(s, "yellow")
bt.onmouseover = () => bt.style.backgroundColor = "orange"
bt.onmouseout = () => bt.style.backgroundColor = "yellow"
return bt
}
// gray button for operators
function operator(s) {
let bt = RB(s, "silver")
bt.onmouseover = () => bt.style.backgroundColor = "#6060FF "
bt.onmouseout = () => bt.style.backgroundColor = "silver"
br() // create line break
return bt
}
Here we apply different colors to the buttons and some event-functions, that create a hover effect. We could have used CSS to create the same effect, which in some cases might be advised. But CSS is limited to styling only, while the functions we apply in our template may also define some operational features (check a value, store it to a database or whatever...). Template functions can be very powerful and apply also some program logic to the DOM elements directly.
The operator function also creates a line break br() after each button.
Both functions return a reference to the newly created button to give access to the newly generated button. Now we want to create our keyboard. In DML you can let a program do this work. First, we create our number block:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, ".", 0]
// build number block
sidiv("", "margin-right: 10px;") // ---> create a box
i = 0
for (n of numbers) {
number(String(n)).onclick = numberClick
if (++i % 3 == 0) br() // line break after 3rd column
}
number("C").onclick = () => { op = "="; display.textContent = ""; buffer = "0" }
unselectBase() // <--- finish box
The buttons are generated within a div. In DML, sidiv() is a combined command, that creades an inline div and sets the insert point into this box. The number buttons are created in a loop from an array "numbers". After every third button we insert a line break. An onclick-event is applied during creation to every button (onclick -> numberClick()) to make the buttons work.
As the C-button (clear) needs a different event, it was not created from the arry, but manually in advance.
The same procedure is used to create the operator buttons. As a line break br() is necessary after each operator button, it was created directly in the template function operator(s)
const operators = ["+", "-", "*", "/", "="]
// Right box for operators
sidiv() // ---> create a box
for (o of operators)
operator(o).onclick = operatorClick;
unselectBase() // <--- finish box
Finish! We have created 2 div´s with 17 functional buttons that build the main part of our Interface. The full application has some additional code to evaluate the operations, which is a bit tricky if you want to mimic the behavoir of a standard calculator.
The full code
This was a short demonstration of the use of "Functional Templates" in DML. The full code is given below:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<script src="https://efpage.de/DML/DML_homepage/lib/DML-min.js"></script>
</head>
<body>
<script> "use strict";
let i, n, o, v, history, display, buffer = "0", op = "=";
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, ".", 0]
const operators = ["+", "-", "*", "/", "="]
// round button
function RB(s, color = "yellow") {
const r = 30;
return button(s, "width:" + px(r) + "; height:" + px(r) + "; border-radius: 50%; background-color: " + color + "; margin: 3px;")
}
// yellow button for numbers
function number(s) {
let bt = RB(s, "yellow")
bt.onmouseover = () => bt.style.backgroundColor = "orange"
bt.onmouseout = () => bt.style.backgroundColor = "yellow"
return bt
}
// gray button for operators
function operator(s) {
let bt = RB(s, "silver")
bt.onmouseover = () => bt.style.backgroundColor = "#6060FF "
bt.onmouseout = () => bt.style.backgroundColor = "silver"
br()
return bt
}
function bval() { return Number(buffer) }
function dval() { return Number(display.textContent) }
// Click on number
function numberClick(e) {
if (op == "=") {
display.textContent = ""
op = ""
}
if (op != "")
if (buffer == "0") {
buffer = display.textContent
display.textContent = ""
}
display.textContent += e.srcElement.textContent
}
// evaluate last function and set as display value
function evaluate() {
switch (op) {
case "+": v = bval() + dval(); break;
case "-": v = bval() - dval(); break;
case "*": v = bval() * dval(); break;
case "/": v = bval() / dval(); break;
default: v = Number(display.textContent);
}
return String(v)
}
// evaluate the operator click
function operatorClick(e) {
let flg = (op != "=") && (buffer != 0)
let o = bval() + op + dval() + "="
display.textContent = evaluate() // evaluate the last operator to display
buffer = "0" // clear buffer
o += display.textContent
if (flg) {
history.value += "\n" + o
history.scrollTop = history.scrollHeight;
}
op = e.srcElement.textContent // set new operator
}
/****************************************************************************************
Build the panels
****************************************************************************************/
// build Main box
sidiv("", _bigPadding + _radius + _box)
// left subbox for numbers
sidiv("Calculator", "margin-right: 10px;"); br()
history = textarea("", { readonly: true, style: "resize: none; font-size: 60%; height: 50px;" })
display = div("", _border + _right + "margin-bottom: 15px; height: 22px;") // result display
// build number block
i = 0
for (n of numbers) {
number(String(n)).onclick = numberClick
if (++i % 3 == 0) br()
}
number("C").onclick = () => { op = "="; display.textContent = ""; buffer = "0" }
unselectBase()
// Right box for operators
sidiv()
for (o of operators)
operator(o).onclick = operatorClick;
unselectBase(2)
</script>
</body>
</html>
Posted on August 8, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.