Last week my colleague Martin approached me and asked how to easily create a Hierachy representation in Filemaker.
I answered him that you could either do it with some trickery in native Filemaker (as described
here http://iconiccoding.com/2021/05/01/sortable-hierachy/), or with a webview with HTML.
Since the native Filemaker solution was a bit too complex for his problem, he asked for the HTML solution.
I first thought of a JQUERY plugin I used some time ago that allows drag and drop moving and sorting.
This would also be a bit overdone for just displaying a hierarchy (but we will discuss this solution in a later article).
So when I was looking for a very simple solution, I stumbled across this excellent article at w3schools.com.
By the way, w3school.com is, next to stackoverflow.com, always my first port of call when I want to look up
something about HTML.
This w3school.com article describes a very smart solution for a hierarchy representation with a few lines of HTML completely without JQUERY or libraries.
If we look at the HTML/CSS and JavaScript, we can quickly see that with one or two adjustments we can generate
the hierarchy entries from Filemaker.
Let’s take a short detour into how to connect records into a hierarchical structure in Filemaker.
Some time ago, my colleague Martin worked on multi-level hierarchies in Filemaker and came up
with a very clever and efficient method.
A hierarchy means nothing else than that records have to know if they are parent or child entries in the tree.
Martin simply used a field ParentId in which the id of the parent element of a record is entered.
Then he created a relationship from the Id of a record to the ParentId of the same table and then created an
(unstored) calculation that always looks at the child records.
With this recursion he could map the whole tree with only one calculation, because the top level element collects
its children via this calculation and these in turn collect their children and so on, all along the branch of the hierarchy tree.
This method is perfect for creating the HTML for our example.
So let’s code !
We need only 2 tables ‘Main’ and ‘Sub’ with very few fields to solve the problem:
Table ‚Main‘
A_1 [calculation] value is always 1
HTML [Text] contains the HTML – You can just as well include the contents of the field in the webview
id [indexed Auto-enter Serial]
Table ‚Sub‘
id [indexed Auto-enter Serial]
idParent [indexed Number] holds the parent Id of this Item
idMain [indexed Number] the corresponding Id of the ‘Main’ table
HTML_li [unstored calculation] generates the hierarchy items
sort [indexed Number] sorting within the level
title [Text] the title of the item
topLevel [indexed calculation] determines if the element is on the top level – is needed to get the top level from the sub table into main
Here is the relationship diagram
I actually just put the w3school HTML, CSS and JavaScript into a text field (you could even put it directly into the webview)
and then used a single placeholder ‘__entries’ in a calculation of the webview to replace the hierachy entries.
The hierachy entries are collected via the ‘Sub Toplevel’ relation from the ‘HTML_li’ calculation field.
The calculation field ‘HTML_li’ determines if this entry contains subentries and then uses the class ‘line’ and ‘onclick=’clickLink(” & id & “)’ ‘ to
make the list item clickable or (if it has child elements) the class ‘nested’ is used and the child elements are collected recusively.
Case ( Count ( Sub#Child::id ) = 0 ; "<li><span class='line' onclick='clickLink(" & id & ")'>" & title & "</span></li>" ; Count ( Sub#Child::id ) > 0 ; "<li><span class='caret'>" & title & "</span>¶<ul class='nested'>¶" &
List ( Sub#Child::HTML_li ) & "¶</ul>"
)
This is actually all we need to do in Filemaker to display the records hierarchically.
All we have to do now is to extend the w3school HTML with a function for calling a Filemaker script when clicking on a ‘line’ entry
function clickLink(p){
FileMaker.PerformScript ( 'ReturnLink', 'clickLink||||' + p );
};
Overall, the HTML then looks like this:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
font-family:verdana;
font-size:12px;
}
ul, #myUL {
list-style-type: none;
}
#myUL {
margin: 0;
padding: 0;
}
.caret {
font-weight: bold;
cursor: pointer;
-webkit-user-select: none; /* Safari 3.1+ */
-moz-user-select: none; /* Firefox 2+ */
-ms-user-select: none; /* IE 10+ */
user-select: none;
}
.caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
.caret-down::before {
-ms-transform: rotate(90deg); /* IE 9 */
-webkit-transform: rotate(90deg); /* Safari */'
transform: rotate(90deg);
}
.nested {
display: none;
}
.active {
display: block;
padding-left: 14px;
}
.line {
margin-left: 16px;
background: none!important;
border: none;
padding: 0!important;
/*optional*/
font-family: arial, sans-serif;
/*input has OS specific font-family*/
color: #069;
text-decoration: underline;
cursor: pointer;
}
.line:hover {
font-weight: bold;
}
</style>
</head>
<body>
<ul id="myUL">
__entries
</ul>
<script>
var toggler = document.getElementsByClassName("caret");
var i;
for (i = 0; i < toggler.length; i++) {
toggler[i].addEventListener("click", function() {
this.parentElement.querySelector(".nested").classList.toggle("active");
this.classList.toggle("caret-down");
});
}
function clickLink(p){
FileMaker.PerformScript ( 'ReturnLink', 'clickLink||||' + p );
};
</script>
</body>
</html>
The content of the webview is then really simple:
Substitute ( Main::HTML ; "__entries" ; List ( Sub Toplevel::HTML_li ) )
As said you could save the text field with the HTML if you put the content directly into the webview.
But for testing a text field is always easier because you can quickly make an adjustment there and immediately see
the result in the webview.
If you now build this into a simple layout, you have a very nice hierarchical structure with just a few fields.
If you like to have an open version of the file, just send me an email.
You have any questions? Feel free to send me an email, I’ll try to answer as soon as possible.