Essentials

TIBET Essentials

Welcome to the second tutorial in our "Hello World!" series.

This tutorial builds on the TIBET Quickstart Guide so if you haven't worked through that tutorial do that first. It's ok, we'll be here when you get back ;).

The concrete steps we'll go through in this tutorial are summarized in the tl;dr section below. You can do the tl;dr version, or follow the full explanations in the 'Step By Step' section.

Here we go…

tl;dr

Start your application using tibet start:

$ tibet start
...

Electron projects are good to go. Server-based projects should load http://127.0.0.1:1407#?boot.profile=development in a supported browser such as Chrome or Edge Chromium.

Create a tag for date/time data using tibet type with --dna computedtag:

$ tibet type hello:now --dna computedtag

Edit ~app_tags/APP.hello.now/APP.hello.now.js so it returns a new element containing the current date/time value when asked to transform:

APP.hello.now.Type.defineMethod('tagCompile',
function(aRequest) {
    var elem;

    //  Get the initiating element.
    if (!TP.isElement(elem = aRequest.at('node'))) {
        return;
    }

    //  Set its content to the date.
    TP.nodeSetContent(elem, (new Date()).toString());

    //  Return the element.
    return elem;
});

Ensure APP.hello.now.js includes an event handler for TIBET's UIActivate signal. This will update the date/time whenever the tag is activated (vi mouse or keyboard). Add the handler using defineHandler as shown here:

APP.hello.now.Inst.defineHandler('UIActivate',
function(aSignal) {
    var target,
        tag;

    target = aSignal.getTarget();
    tag = TP.wrap(target);

    tag.set('value', new Date().toString());
});

Edit the ~app_tags/APP.hello.app/APP.hello.app.xhtml template to hold an instance of both our hello:world and new hello:now tags:

<div tibet:tag="hello:app">
    <hello:world/>
    <hello:now/>
</div>

Once you've made the previous changes your screen should immediately refresh to show both Hello World! and the date/time at the time the page rendered:

hello:now implementation
<hello:now/>

Thanks to the event handler we created for UIActivate, clicking the date/time should automatically refresh the displayed data.

That's a lightning-fast intro to TIBET computed tags and event handling. Obviously there's a lot more to each of these subjects but hopefully this example further shows how TIBET lets you focus on tags and types with minimal coding.

For a full discussion of each of the previous steps read on.

Step By Step…

Before we dive deeply into our next steps 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 after working through the Quickstart Guide you should see something like:

Project Home Page
A Whole Lotta Hello

With our project back up and running it's time to tackle the next step…

Precision

Computed Tags

When you need more precision in rendering your tags than you can get with templating, TIBET offers another approach to tag authoring using what we call 'computed tags'.

A 'computed' tag in TIBET is a tag whose rendered markup is produced by JavaScript rather than an external template file.

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/>.

We could do this task using handlebars-style 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 and associated test file, hot patching them into our application. No template file will be created since this is a computed tag.

To keep things simple 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 once and tell the time.

<div tibet:tag="hello:app">
    <hello:world/>
    <hello:now/>
</div>

~app_tags/APP.hello.app/APP.hello.app.xhtml

Save your template with the new hello:now tag and you should see:

hello:now first try
<hello:now/> version 0

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 tagCompile method.

Click that <hello:now/> link:

hello:now link alert
Hello Now alert()

Newly created computed tags running outside of the Lama™ will alert() you to update the tag's tagCompile method, a simple way of helping you to work iteratively from definition through refinement. (In the Lama you'd be taken directly to that method for editing).

As the link alert suggests, let's refine the hello:now tagCompile method next.

tagCompile

When TIBET processes tags it invokes a number of methods on the types which support those tags. The tagCompile method is perhaps the most commonly used of those since it's the method responsible for converting the authored form of the tag into a renderable form.

We'll start by taking a look at the tagCompile stub generated by tibet tag in the source file ~app_tags/APP.hello.now/APP.hello.now.js.

Open ~app_tags/APP.hello.now/APP.hello.now.js, our hello:now type's source:

/**
 * @type {APP.hello.now}
 * @summary A computed tag which...
 */

//  ------------------------------------------------------------------------

TP.core.ComputedTag.defineSubtype('APP.hello:now');

//  ------------------------------------------------------------------------

APP.hello.now.Type.defineMethod('tagCompile',
function(aRequest) {

    /**
     * @method tagCompile
     * @synopsis Convert instances of the tag into their XHTML form.
     * @param {TP.sig.Request} aRequest A request containing the tag element
     *     to convert along with other optional processing parameters.
     * @returns {Element|Array<Element>} The element(s) to replace the inbound
     *     element with in the final DOM.
     */

    return this.callNextMethod();
});

//  ------------------------------------------------------------------------
//  end
//  ========================================================================

APP.hello.now.js

Since this is our first deep look at 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.core.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. In this case we create a new TP.core.ComputedTag subtype named 'APP.hello.now'.

Line 2

APP.hello.now.Type.defineMethod('tagCompile',

We begin the actual implementation by defining a method on our new hello:now type to handle tagCompile processing. The first parameter to defineMethod is 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-called `method methods` is a best practice as described in JavaScript: The Good Parts

In this case we want our tagCompile 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. 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(aRequest) {

With defineMethod's second parameter we're providing the method body, the function that will do the work of transforming the tag. Virtually all tag processing methods take a single parameter, a TIBET Request object, 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.

TIBET is unabashedly Object-Oriented. As a result we often need 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.core.ComputedTag that type will be checked for tagCompile 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.

Many popular OO languages don't have true type inheritance, they have 'static methods'. That's not quite the same since static methods are local to a type and don't support interitance.

TIBET's OO infrastructure is modeled on Smalltalk, with a dash of Traits thrown in to support composition in a predictable, controllable fashion. TIBET lets types inherit methods and attributes, just like instances.

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.

tagCompile v2

In the version of our tagCompile method below we've replaced our stub's callNextMethod() logic with a full implementation.

Note that there's no constraint on what the `tagCompile` method does, only that it return whatever node (element, document fragment, etc) forms the replacement content.

Edit your version of APP.hello.now.js to include this implementation:

APP.hello.now.Type.defineMethod('tagCompile',
function(aRequest) {
    var elem;

    //  Get the initiating element.
    if (!TP.isElement(elem = aRequest.at('node'))) {
        return;
    }

    //  Set its content to the date.
    TP.nodeSetContent(elem, (new Date()).toString());

    //  Return the element.
    return elem;
});

APP.hello.now.js

Let's look at this chunk by chunk.

Get the initiating element

The first step is to access the node in the document representing our authored tag. The request parameter's at method can give us that by requesting the node property. We verify we get a valid element from the request using the TP.isElement() primitive:

// Get the initiating element.
if (!TP.isElement(elem = aRequest.at('node'))) {
    return;
}

Set the content of the element

Next we set the content of the element to be the String representation of a new Date:

// Set its content to the date.
TP.nodeSetContent(elem, (new Date()).toString());

Return the element to be rendered.

Finally, we return the node (typically an Element or DocumentFragment) to be rendered in place of the original node. In this case we return the original element since we don't need a replacement node, we just want to update the current element's content in place.

//  Return the element.
return elem;

Save these changes and our screen should now display:

hello:now implementation
<hello:now/>

Success!

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.core.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, the tagCompile method.

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, the next step is to add behavior ("feel") to our tag.

Tag Behavior

Tag Behavior

When we think of behavior in JavaScript terms we're typically thinking about how an object responds to events. Event-driven development is central to web development, particularly client-side web development.

A key feature of TIBET is its signaling subsystem, a set of components which unify how events work across browsers (and across the main and renderer processes in Electron) while also supporting non-native events. In TIBET you work with Signal instances for DOM events, Exceptions, state changes, etc.

For this tutorial our goal is to add reusable behavior to our hello:now tag so it will update its date/time display any time we activate (click) it. We do that via signal handling.

defineHandler

To define a signal handler we use the defineHandler method, a variant of TIBET's defineMethod method that's specific to signal handlers. As with defineMethod the first parameter is the handler name and the second parameter is the handler body (function).

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 that 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');
});

APP.hello.now.js

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 and is one of a number of 'UI Signals' TIBET provides to enhance accessibility and testability across browsers and input devices.

Note that we don't need to set up or tear down observations or do any other boilerplate coding. 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:

UIActivate
hello:now UIActivate stub

With this simple action we've confirmed our event handler is operational.

Notice that we didn't reload, 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) {
    var target,
        tag;

    target = aSignal.getTarget();
    tag = TP.wrap(target);

    tag.set('value', new Date().toString());
});

APP.hello.now.js

In the code above we access the signal's target via getTarget. This is the low-level node that received the initial event, much as we'd expect from a normal DOM event handler.

The next line 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, this would set its .value, property but because our target here is an inline, non-form element, setValue instead sets its content (innerHTML in this case).

Save the new handler definition and 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 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

Summary

This "essentials" guide built upon the foundation created in the TIBET Quickstart Guide.

To kick things off we added a new custom hello:now tag which leverages a JavaScript method to render the current date and time.

Next we expanded our hello:now tag's functionality by adding behavior to all hello:now tags so they update their display whenever they receive a UIActivate signal.

In particular, we updated tag behavior without any add/remove listener overhead, reducing boilerplate and the potential for memory leaks, duplicate, or dangling listener registrations.

TIBET has a lot more to offer but hopefully this further exploration of a simple hello application has helped reinforce one of the core things that makes TIBET special, namely a development process focused on tags, not code.

We invite you to continue to explore TIBET by checking out the entire TIBET platform, particularly the TIBET Lama, our revolutionary web development IDE.