TIBET Essentials - Part 3
Welcome to Part 3 of our TIBET Essentials series.
In this installment we'll focus on:
- sharing state using TIBET URNs as value holders
- using the initialize method to perform one-time setup
- signaling, observing, and responding to notifications
Our Hello World! app can display a date/time in response to UIActivate
events
but that date/time display doesn't update automatically.
We could just add a timer to update it every second, however to make things more
interesting we're going to add a tag that will reflect how long it's been since
we last updated the hello:now
tag's date/time display. We'll then update our
new tag's delta display in response to a regular timer signal.
Let's get started…
Preliminaries
Make sure your hello
project is running.
Open a terminal, cd to your project home, and execute tibet start
:
cd ${project_home}
tibet start
...
Having completed TIBET Essentials - Part 2 you should see:

With our project back up and running we're ready for new code.
hello:again - Part 1
We'll start off by creating a new tag to display the timestamp delta.
By now you should be familiar with the tibet type
command.
Create a new hello:again
tag using computedtag
dna:
tibet type hello:again --dna computedtag
Next we'll add our new hello:again
tag to the hello:app
template file
(~app_tags/APP.hello.app/APP.hello.app.xhtml
):
<div tibet:tag="hello:app">
<hello:world/>
<hello:now/>
<hello:again/>
</div>
As always, TIBET will automatically update our current view:

We can style the new tag so it lays out a little more cleanly.
Edit ~app_tags/APP.hello.again/APP.hello.again.css
to contain:
@namespace hello url("urn:app:hello");
hello|again {
display: block;
margin: 2em;
}
TIBET auto-refreshes and we now should see:

So far so good.
Design Overview
Let's take a moment to outline our concrete plan.
In response to hello:now
timestamp updates we want our hello:again
tag
to reset the delta and refresh.
To achieve that goal we'll need to keep the time of the last timestamp update
somewhere both the hello:now
and hello:again
tags can access it.
When the hello:now
tag is rendered/refreshed we'll need to update that data
location with the new timestamp.
To keep the delta value "current" we'll also need the hello:again
tag to
respond to a regular timer signal (perhaps via setInterval) to keep the delta
updating between activations.
Let's start by tracking hello:now
's rendering timestamp in a shareable location.
The best place to do that is in a TIBET URN.
hello:now - Data Sharing
A TIBET URN is essentially a named data container. Once created you can access the container by name from anywhere in your code (or templates).
URNs must use urn:
as their initial string prefix. That value is then
followed by what the URN standard refers to as the Namespace ID (NID) and
Namespace-specific string (NSS).
urn:{NID}:{NSS}
TIBET doesn't enforce global NID uniqueness or persistence; you can use whatever works best for your application.
By convention we tend to use tibet
for lib URNs, app
for generic app URNs,
and your application's namespace (hello
in this case) for custom app URNs.
We'll use a NID of hello
for this example so our URNs will be prefixed with
urn:hello:
and then a specific name for our data item.
Initial Render
We want to store the initial rendering timestamp so update the hello:now
tag's
getExpandedSource
type method to contain the following:
APP.hello.now.Type.defineMethod('getExpandedSource',
function(tpElement, aRequest) {
const timestamp = new Date().toString();
TP.uc('urn:hello:now-timestamp', timestamp);
return timestamp;
});
The TP.uc
call, a shortcut to TP.uri.URI.construct
, gives us a handle to the
URN instance identified by urn:hello:now-timeout
.
If a URN doesn't exist yet it's created. If it does exist we get a handle to the unique instance, allowing us to share it across the entire application.
The second parameter to the TP.uc
call can optionally provide an initial
value. For the initial creation we use that to initialize the URN's value.
Activations
In addition to the initial render, we want to track click/keyup refreshes.
We can do that by updating the hello:now
tag's UIActivate
instance signal
handler to update the URN value as well.
Note that in the following code we use the URN's setResource
method to ensure
our update triggers TIBET's built-in change notification machinery.
APP.hello.now.Inst.defineHandler('UIActivate',
function(aSignal) {
const timestamp = new Date().toString();
const tag = TP.wrap(aSignal.getTarget());
tag.set('value', timestamp);
const urn = TP.uc('urn:hello:now-timestamp');
urn.setResource(timestamp);
});
URNs are automatically managed and uniqued by the TIBET runtime so we don't need
to store the URN in a variable, constant, or other location.
With those two updates we've completed the work of saving the last update timestamp and ensuring any observers will be notified when it changes.
Our next task is to update the hello:again
tag to render and respond to
changes to our URN's value.
hello:again - Part 2
Tag Rendering
Our hello:again
tag is currently displaying a default link when rendered.
Let's update it to render the value of our new URN, making it reflect the value it finds when it initially renders.
Edit ~app_tags/APP.hello.again/APP.hello.again.js
to include:
APP.hello.again.Type.defineMethod('getExpandedSource',
async function(tpElement, aRequest) {
const result = await TP.uc('urn:hello:now-timestamp').getResource();
return result.getValue();
});
Note that we await
the result of invoking getResource
since URIs can acquire
their value from a variety of potentially async sources.
Once we have the result
we ask for its value via getValue()
and return.
Saving our changes above should result in the following display:

With that change we've got our hello:again
tag rendering the URN value. But it
will only do it once since it's not yet instrumented to respond to changes.
If you click on hello:now
you'll see that the timestamp for hello:now
changes but our hello:again
tag doesn't update.
Our next step is to help it stay current with any changes to hello:now
.
Change Notification
The majority of TIBET objects include the machinery necessary to signal that
an attribute (aspect) has changed via their set()
-related methods.
That includes TIBET URIs, URLs, and URNs which is why we used setResource
to
set the resource (aka value) of our URN.
The thing is, while our URN is signaling changes each time we activate
hello:now
, there's currently nobody listening.
Let's fix that by updating our hello:again
implementation to observe our URN.
We'll do this once, on initial app startup, in our type's initialize
method.
Edit ~app_tags/APP.hello.again/APP.hello.again.js
and add the following:
APP.hello.again.defineMethod('initialize',
function() {
this.observe(TP.uc('urn:hello:now-timestamp'), 'ValueChange');
});
TIBET's signaling system lets any object
receive signals from any other object by invoking the observe()
method.
Using the type's initialize
method means we only invoke observe()
once,
avoiding the possibility of duplicate observations.
Note that we authored
initialize
above as atype local
method, meaning that there's no.Type
or.Inst
qualifier after the type name. The method is placed on the individual type and won't be inherited by subtypes.
Signal Handling
You might have noticed our observe
call didn't define a handler function.
TIBET doesn't restrict handlers to functions, they can also be objects.
The observe
call defaults to making the invoking object the handler.
When a signal is dispatched any non-function object registered as a handler is checked for the best possible handler method to use via reflection.
Since we invoked observe
from the APP.hello.again
type the type itself is
the handler and it will be checked for methods targeting ValueChange
.
As it turns out, all TP.tag.CustomTag
subtypes implement a Type-level handler
for ValueChange
that automatically updates any rendered instances.
Running The Initializer
Since we added/altered an initialize
method and those are only invoked once on
application startup we have two options for getting our observation to go live:
- reload the application (but reloads lose context)
- invoke it directly via the DevTools console or Lama
To do the latter enter the following snippet in the DevTools console:
APP.hello.again.initialize();
Once the initializer has run and the observe
has executed clicking the
hello:now
tag should automatically update the display of our hello:again
tag.

Delta Time
Our final task(s) relate to the display of our hello:again
delta time.
Display
We really haven't been producing a delta time so far, we've been just mirroring the timestamp value. Let's fix that.
Edit ~app_tags/APP.hello.again/APP.hello.again.js
to contain:
APP.hello.again.Type.defineMethod('getExpandedSource',
async function(tpElement, aRequest) {
const result = await TP.uc('urn:hello:now-timestamp').getResource();
const last = new Date(result.getValue()).getTime();
return `${Date.now() - last}`;
});
Now our display should resemble the following:

Clicking hello:now
repeatedly will update the delta display depending on
varying latencies, but it still doesn't update automatically.
Update
Our final step is to trigger a refresh of the delta time on a regular basis.
We can do the first half by triggering an update signal on a timer.
Since we only need a single, consistent heartbeat signal, we can add that
portion of the logic to our previous initialize
method:
APP.hello.again.defineMethod('initialize',
function() {
const urn = 'urn:hello:now-timestamp';
this.observe(TP.uc(urn), 'ValueChange');
this.observe(urn, 'Heartbeat');
setInterval(() => { TP.signal(urn, 'Heartbeat') }, 1000);
});
TIBET signaling is extremely flexible, letting you signal using any string or object as an origin and letting you handle those signals from any object or function.
Here we've updated our initialize
to observe the string (not URN)
urn:hello:now-timestamp
for Heartbeat
signals in addition to the previous
ValueChange
observation.
We've also configured an interval to signal Heartbeat
from any origin matching
the URN string roughly every second.
As before, we need to manually invoke any initialize
method we add or alter
from the DevTools console (or Lama):
APP.hello.again.initialize();
Our Heartbeat
signal is now running but there's no real handler for it yet.
Let's add a simple event handler that will refresh all instances of our tag.
Since we invoked observe
from the type add this type method:
APP.hello.again.Type.defineHandler('Heartbeat',
function(aSignal) {
this.refreshInstances();
});
With this final change the delta display should automatically begin updating every second or so.
Clicking the hello:now
tag will reset the display to something under a second
and it'll go back to counting up from there.
Summary
In this third installment of TIBET Essentials we:
- worked with a TIBET URN to share state across components
- implemented a type
initialize
method for one-time setup - created signal handler logic for responding to value changes
- created a signal handler for a generic heartbeat signal
We hope you've enjoyed the TIBET Essentials series so far.
If there are additional topics you'd like to see let us know!
Enjoy your TIBET journey!