TIBET Essentials - Part 2
Welcome to Part 2 of our TIBET Essentials series.
This tutorial builds on the TIBET Quickstart and TIBET Essentials - Part 1 guides.
Start with those. We'll be here when you get back :).
In this guide we'll focus on:
- 'computed' tags, tags which render via JavaScript logic
- adding behavior to tags using TIBET's
defineHandler
method
Here we go…
Preliminaries
Before we dive into things, make sure your hello
project is running.
Open a terminal, cd to your project home, and execute tibet start
:
cd ${project_home}
tibet start
...
If you haven't changed anything since completing the previous guide you should see something like this:

With our project back up and running it's time to tackle the next step.
Computed Tags
A computed tag is a tag whose rendered markup is produced by JavaScript rather than an external template file.
Computed tags provide for more powerful logic-driven rendering that a pure template can provide, yet you can still consume them using 100% markup.
hello:now
To create a simple example of a computed tag let's build another custom tag,
this time one that outputs the current date/time when it renders. We'll call
this tag <hello:now/>
.
NOTE: We could do this task using substitution syntax in a templated tag but for purposes of this tutorial we'll create a computed tag instead.
Create a new computed tag by using --dna computedtag
with the tibet type
command:
$ tibet type hello:now --dna computedtag
In response to this command TIBET will generate a tag type, stylesheet, and test file, live patching them into our package and application.
Note that no XHTML template will be created when using
computedtag
dna.
Let's add our new tag to the <hello:app/>
template, adjusting it so we get
both <hello:world/>
and <hello:now/>
output in our home page.
Edit the APP.hello.app.xhtml
template to say hello and tell the time.
<div tibet:tag="hello:app">
<hello:world/>
<hello:now/>
</div>
Save your template with the new hello:now
tag and you should see:

What just happened?
We created a new computed tag, added it to our app tag template, and we get a link identifying the tag location in our UI.
That link is an indication our new type is working as expected. What we're
seeing is the default implementation of a computed tag's rendering logic,
inherited from TIBET's TP.tag.CustomTag
supertype.
Click that <hello:now/>
link and you'll see an alert similar to:

Computed tags running outside of the Lama™ will
alert()
you to update the tag's getExpandedSource
or getExpandedNode
method.
When the Lama is active you're taken directly to an in-situ code editor.
As the link alert suggests, let's refine the getExpandedSource
method next.
getExpandedSource
TIBET's tag processor provides a number of entry points for generating your tag UI.
The tagExpand
is the method used by TIBET's tag processor and it defers to
getExpandedNode
to get a renderable DOM Node. That method defers to
getExpandedSource
to get a parsable DOM String.
You can theoretically override any one of these methods but it's best to stick
to getExpandedSource
for the majority of operations.
The tibet type
command's DNA for a computed tag includes a stub for getExpandedSource
in the tag's source file to make editing it easier.
Open ~app_tags/APP.hello.now/APP.hello.now.js
, our hello:now
type's source:
// ========================================================================
/**
* @copyright Copyright (C) 2022, the AUTHORS. All Rights Reserved.
*/
// ========================================================================
/**
* @type {APP.hello.now}
* @summary TP.ux.ComputedTag subtype which...
*/
// ------------------------------------------------------------------------
TP.ux.ComputedTag.defineSubtype('APP.hello:now');
// ------------------------------------------------------------------------
// Type Methods
// ------------------------------------------------------------------------
APP.hello.now.Type.defineMethod('getExpandedSource',
function(tpElement, aRequest) {
/**
* @method getExpandedSource
* @summary Creates and returns a string or pair representing the string
* expansion of the tpElement parameter provided. This is the core
* method most subtypes will override to do their expansion work.
* @param {TP.dom.Element} tpElement A wrapped instance to be expanded.
* @param {TP.sig.Request} aRequest A request containing processing
* parameters and other data.
* @returns {String|Array} The string or ordered pair result from expansion.
* When using an ordered pair return the new string and a processing
* hint (TP.UPDATE, TP.REPLACE).
*/
return this.callNextMethod();
});
// ------------------------------------------------------------------------
// Instance Methods
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// end
// ========================================================================
Since we're just getting warmed up with TIBET code let's take it a line at a time. It's a heavily-commented file with only 5 lines of executable code.
Line 1
TP.ux.ComputedTag.defineSubtype('APP.hello:now');
Much like our earlier look at the hello:app
source file, the first line in
many TIBET files will be a line that invokes defineSubtype
to create a new
type.
As mentioned elsewhere, TP
is the global root for all TIBET types and
variables. APP
is the global root for all your application types and code.
The ux
namespace is TIBET's default namespace for UX tags such as our
TemplatedTag
and ComputedTag
supertypes.
In this case we create a new TP.ux.ComputedTag
subtype named 'APP.hello.now'.
Line 2
APP.hello.now.Type.defineMethod('getExpandedSource',
We begin the actual implementation by defining a method on our new hello:now
type.
The first parameter to defineMethod
is always the method name.
TIBET methods are always defined using the
defineMethod
method, one of a set of meta-methods TIBET uses to help manage Type definition. Using so-calledmethod methods
is a best practice as described in JavaScript: The Good Parts™
In this case we want our getExpandedSource
method to be a type method so we add
a .Type
qualifier to target the type.
If we wanted an instance method we'd add .Inst
to target the instance prototype.
If we wanted a 'local' method, a method unique to a single object, we leave off
the 'Type' or 'Inst' qualifier and invoke defineMethod
directly on the
targeted object.
Line 3
function(tpElement, aRequest) {
With defineMethod
's second parameter we're providing the method body, the
function that will do the work of transforming the tag.
Most common tag processing methods take the current tag instance (tpElement
)
and current processing request (aRequest
), as shown above.
NOTE: Do not use arrow functions for method bodies. Arrow functions have unique binding behavior that is incompatible with object-oriented binding of TIBET methods. Arrow functions are best reserved for iterators in TIBET.
Line 4
return this.callNextMethod();
Now things get a little more interesting.
A common requirement in OO is the ability to invoke the supertype version of a method. In this case we're asking our stub to do just that, to call 'the next method' up the chain without requiring hard-coded references or duplicating the argument list.
Since we subtyped TP.ux.ComputedTag
and this is a .Type
method, that type
will be checked for getExpandedSource
and the search will continue up through
the type hierarchy until an implementation is found.
If you're used to C++, Java, or other JS libraries that statement should give you pause.
JavaScript, like many of today's popular OO languages, doesn't have true type-level inheritance, it has 'static' methods and attributes.
Static methods and attributes are local to a type and are not inherited by subtypes, limiting the patterns of logic flow and code reuse.
TIBET's OO infrastructure is modeled on Smalltalk, with a dash of Traits thrown in to support composition in a predictable, controllable fashion.
TIBET's callNextMethod
logic first looks to the immediate type or instance,
then works up the inheritance/trait chain to find the proper method regardless
of the nature of the receiver.
Line 5
});
Close method body, close defineMethod
parameter list, end statement.
Done.
With our review of the stub implementation complete our next task is to create a
real one.
getExpandedSource v2
In the version of our getExpandedSource
method below we've replaced our stub's
callNextMethod()
logic with a sample implementation.
NOTE: there's no strict constraint on what the
getExpandedSource
method does, only that it return a string which can produce a valid XML Node (node, not element…you can create text nodes, comment nodes, whatever you like).
Edit your version of APP.hello.now.js
to include this implementation:
APP.hello.now.Type.defineMethod('getExpandedSource',
function(tpElement, aRequest) {
return new Date().toString();
});
Save these changes and our screen should now display:

Success!…
…but our date string is a little too close to that left edge.
Let's refine our look a little more with some CSS.
Styling hello:now
In our previous guide we showed how to style a templated tag whose authored XML
had been transformed into XHTML (an h1
in particular).
Our hello:now
tag doesn't transform however, it stays in its original
namespace-qualified XML format as <hello:now>...</hello:now
with a child text
node.
So how do we adjust its style?
By using the standard CSS syntax for XML.
Edit the APP.hello.now.css
file so it contains:
/**
* @overview 'APP.hello.now' styles.
*/
@namespace hello url("urn:app:hello");
hello|now {
margin-left: 2em;
}
Note the vertical bar (|) where the colon (:) would be in a tag (or attribute) name.
CSS manages namespaced content by replacing :
with |
for element and
attribute names.
It's critical you include an
@namespace
entry for any namespace you refer to… otherwise you'll be wondering why your style isn't being applied :).
With our style applied we now see the following:

Testing hello:now
Let's run our tests again. This time let's just test our new tag…
Change to an available terminal and enter tibet test APP.hello.now
:
$ tibet test APP.hello.now
# Loading TIBET platform at 2019-11-09T17:43:17.668Z
# TIBET reflection suite loaded and active in 5219ms
# Running Type tests for APP.hello.now
# TIBET starting test run
# 1 suite(s) found
1..1
#
# tibet test APP.hello.now.Type --suite='APP.hello:now suite'
ok - Is a TP.ux.ComputedTag tag.
# pass: 1 total, 1 pass, 0 fail, 0 error, 0 skip, 0 todo, 0 only.
#
# PASS: 1 total, 1 pass, 0 fail, 0 error, 0 skip, 0 todo, 0 only.
# Running Inst tests for APP.hello.now
# TIBET starting test run
0..0
# PASS: 0 pass, 0 fail, 0 error, 0 skip, 0 todo.
# Running Local tests for APP.hello.now
# TIBET starting test run
0..0
# PASS: 0 pass, 0 fail, 0 error, 0 skip, 0 todo.
# Finished in 4270 ms w/TSH exec time of 204 ms.
All good. We just tested a specific type. But how?
Like virtually all of TIBET, TIBET's test harness isn't page-based, it's object-based.
The tests TIBET generates when we create a new type are associated with that type.
Running tibet test {typename}
tells TIBET to load our application and use
reflection to run any tests it finds.
By associating tests with objects you can keep your testing focused, improve cycle times, and support smarter forms of code coverage analysis.
Recap
Using the tibet type
command (with a --dna computedtag
option) we've created
a new tag that renders based on a JavaScript method on the tag type.
Our current implementation simply outputs the current Date.now()
value but
complex logic is easy to support using this approach.
With the look in place, it's time to add behavior/feel.
Tag Behavior
When we think of behavior we're typically thinking about how an object responds to events.
A key feature of TIBET is its signaling subsystem, infrastructure which unifies how events work across browsers (and across the main and renderer processes in Electron).
In TIBET you work with Signal instances for DOM events, exceptions, state changes, and any other event you wish to create.
For this tutorial we want our tag to update its date/time display any time we activate (click or keyup) it.
We do that by defining a signal handler.
defineHandler
To define a signal handler we use another of TIBET's meta-methods, the defineHandler
method.
As with defineMethod
, the first parameter to defineHandler
is a name (the
signal name to be handled) and the second parameter is a function (the handler
body).
Because we want to add behavior to hello:now
tags we will define our event
handler in the JavaScript source file specific to that tag. This helps keep
functionality organized.
NOTE: TIBET does not require all functionality for a type to be defined in a single file. You are free to alter or extend features of a tag in separate files.
Edit ~app_tags/APP.hello.now/APP.hello.now.js
and add the following handler:
APP.hello.now.Inst.defineHandler('UIActivate', function(aSignal) {
alert('UIActivate');
});
In the code above we define a signal handler for UIActivate
signals received
by instances of hello:now
tags (thanks to the .Inst
qualifier).
Recall that type methods use
.Type
, instance methods use.Inst
, and local methods use no qualifier. In all cases the targeted object receives the method.
If you're wondering what UIActivate
is, it's a TIBET signal which generalizes
click
and keyup
.
UIActivate is one of a number of 'UI Signals' TIBET provides to enhance accessibility and testability across browsers and input devices.
All hello:now
tags will automatically respond to UIActivate
simply by virtue
of our defining a handler for that signal in our tag implementation.
Save your changes, then click on the text of the hello:now
tag in your UI:

With this simple action we've confirmed our event handler is operational.
Notice that we didn't reload, redraw, or refresh.
TIBET hot-patched our tag implementation with the new functionality and all previously-rendered instances of the tag automatically get the new behavior.
Signal Data
We still need to make our tag update its content in response to activation. We
do that by leveraging data in the Signal
instance provided to the signal
handler.
Edit ~app_tags/APP.hello.now/APP.hello.now.js
again, updating the handler to match:
APP.hello.now.Inst.defineHandler('UIActivate', function(aSignal) {
const tag = TP.wrap(aSignal.getTarget());
tag.set('value', new Date().toString());
});
In the code above we access the signal's target via getTarget
. This is the
low-level element node that received the initial event, much as we'd expect from
a normal DOM event handler.
The TP.wrap
call wraps that low-level element in the best-fit TIBET type, in
this case an instance of our APP.hello.now
type, granting access to the
tag's instance methods.
The final line leverages TIBET's 'getter/setter' syntax via set()
, ultimately
triggering a call to setValue
on our tag.
Note that this method relies on polymorphism in that setting the value
of
different tag types has consistent semantics but unique results.
For an input field, set('value')
would set its .value
, property but because
our target here is an inline, non-form element, setValue
instead sets its
child content.
Save the new handler definition, then click on the date in the hello:now
tag.
The hello:now
tag should display the current time each time we click.
It's that easy.
Recap
In this section we used the defineHandler
method to add a signal handler
to instances of our hello:now
tag. Our new signal handler updates the tag's
time display whenever it receives a UIActivate
signal, expanding the value of
our reusable component.
As with our previous efforts we didn't have to reload the page to activate this behavior, we simply edited the source file and saved our changes. TIBET did the rest.
Of special interest is that clicking on a previously rendered tag "just worked". There was no need to set up/tear down listeners or do other boilerplate coding for events.
Summary
This guide built upon the TIBET Quickstart and TIBET Essentials - Part 1 guides.
First we added a new custom hello:now
tag which leverages JavaScript to render
the current date and time.
Next we expanded hello:now
functionality by adding a custom signal
handler for refreshing the date/time display in response to UIActivate
signals.
We accomplished all that without add/remove listener overhead, reducing boilerplate and the potential for memory leaks, duplicate, or dangling listener registrations.
Continue on to TIBET Essentials - Part 3 to explore sharing state across components.