3. Conditional Bindings

This tutorial introduces functionality for conditionally selecting the value of a node.

3.1. Case Operator

The special case operator selects the value of the first node for which the value of the corresponding condition node is true. The case operator is special in that it has a special syntax to make it more readable.

[Tip]

The case operator is actually a macro-node, implemented in Tridash, which expands to a series of nested if functor expressions. You can views its source in the modules/core/macros.trd file of your Tridash installation.

Syntax. 

case(
  condition-1 : value-1,
  condition-2 : value-2,
  ....
  default-value
)

Each argument is of the form condition : value where condition is the condition node and value is the corresponding value node. The last argument may also be of the form value, that is there is no condition node, in which case it becomes the default or else value.

The case functor node evaluates to the value of the value node corresponding to the first condition node which has a true value (equal to the value of the builtin node True), or the value of the default node, if any, when all condition nodes have a false (equal to the value of the builtin node False) value.

Example. 

case(
  a > b : a - b
  b > a : b - a
  0
)

If the node a > b evaluates to true, the case node evaluates to the value of a - b, otherwise if b > a evaluates to true, the case node evaluates to the value of b - a. If neither a > b nor b > a evaluate to true, the case node evaluates to 0.

If the default value node is omitted and no condition node evaluates to true, the case node evaluates to a failure value (you will learn about failure values in a later tutorial which introduces error handling).

3.2. Example 1: Maximum of Two Numbers

Let’s write a simple case expression which returns the maximum of two numbers, a and b, and returns the string “neither” when neither number is greater than the other.

The case expression should evaluate to:

  1. a if a > b
  2. b if b > a
  3. The string “neither” otherwise

These conditions are implemented by the following case expression:

case(
    a > b : a,
    b > a : b,
    "neither"    1
)

1

This is the literal string “neither”.

[Tip]

String constants are written in double quotes "...".

Notice that the last argument does not have an associated condition. The case node evaluates to this argument if none of the conditions, of the previous arguments, evaluate to true.

We can incorporate this in a simple application, which displays the maximum of two numbers entered by the user, using the following HTML interface:

ui.html

<?
 /import(core)

 maximum <-
     case (
         a > b : a,
         b > a : b,
         "neither"
     )
?>
<!doctype html>
<html>
    <head>
        <title>Maximum</title>
    </head>
    <body>
      <h1>Maximum</h1>
      <div><label>A: <input value="<?@ to-int(a) ?>"/></label></div>
      <div><label>B: <input value="<?@ to-int(b) ?>"/></label></div>
      <hr/>
      <div><strong>The maximum of <?@ a ?> and <?@ b ?> is <?@ maximum ?>.</strong></div>
    </body>
</html>

[Tip]

The <- operator is the same as the -> operator however with the arguments reversed, that is b <- a is equivalent to a -> b.

The interface consists of two text fields, the contents of which are bound to nodes a and b. The to-int operator is used to convert the string values to integers as in the previous tutorial.

The node maximum is bound to the value of the case functor, and its value is displayed in an unnamed HTML element below the input fields.

[Note]

The values of a and b are also displayed below the input fields. This is to demonstrate that there is no limit to how many nodes can be bound to a particular node.

Build and run the application, using the same build configuration file and command from the previous tutorials.

Enter some numbers in the text fields:

A: 10, B: 15, The maximum of 10 and 15 is 15

Notice that the maximum, 15 in this case, is displayed below the text fields. Also notice that the values entered in the text fields are displayed as part of the message.

Now change the number, which is the maximum, to a different value which is still greater than the other number:

A: 10, B: 17, The maximum of 10 and 17 is 17

The new maximum is displayed. This demonstrates that if the values of the value nodes, of the case expression change, the value of the case expression is updated.

Change the maximum number such that it is smaller than the other number:

A: 10, B: 6, The maximum of 10 and 6 is 10

This shows that the value of the case expression is also updated if the values of the condition nodes change.

Now finally change the numbers such that they are both equal:

A: 10, B: 10, The maximum of 10 and 10 is neither

The displayed maximum is “neither” which is the default value of the case expression.

3.3. Example 2: Sum Limit

Let’s extend the application developed during the previous tutorial by adding the functionality for specifying a limit to the sum of the two numbers. The application should inform the user of whether the limit was exceeded.

Start with the following slightly modified code from the previous tutorial.

<?
 /import(core)

 a + b -> sum
?>
<!doctype html>
<html>
    <head>
        <title>Sum Limit</title>
    </head>
    <body>
      <h1>Sum Limit</h1>
      <div><label>Limit: <input value="<?@ to-int(limit) ?>"/></label></div>
      <hr/>
      <div><label>A: <input value="<?@ to-int(a) ?>"/></label></div>
      <div><label>B: <input value="<?@ to-int(b) ?>"/></label></div>
      <hr/>
      <div><strong>A + B = <?@ sum ?></strong></div>
    </body>
</html>

A new text input field for the limit has been added, with its value bound to the node limit.

[Note]

The sum a + b is bound to the node sum in order to facilitate the implementation of the new features.

The message “Within limit.” should be displayed if the sum is less than the limit (sum < limit), and “Limit Exceeded!” otherwise. This can be implemented using the following case expression, which is bound directly to an unnamed element.

Add the following below the element where the sum is displayed.

<div>
  <?@
    case(
        sum < limit : "Within Limit.",
        "Limit Exceeded!"
    )
  ?>
</div>
[Note]

There is no difference in efficiency between using the sum node or a + b directly. The value of a node is only computed once, whenever one of its arguments changes, even if it is referenced in more than one location. Moreover the value of a node is not computed if it is not used anywhere.

Build and run the application, and enter some initial values for the limit, a and b.

Limit: 10, A: 8, B: 3, A + B = 11, Limit Exceeded!

“Limit Exceeded!” is displayed since the sum of 11 did indeed exceed the limit of 10, with the numbers in the snapshot above.

Now try increasing the limit:

Limit: 20, A: 8, B: 3, A + B = 11, Within Limit.

The message changes to “Within Limit.”.

3.4. Improvements

Whilst the application we’ve implemented so far demonstrates the power of functional bindings, it is rather lacking in that whether the limit has been exceeded or not is only indicated by text. The text has to be read in full to determine whether the limit was exceeded, and changes from Within Limit to Limit Exceeded, and vice versa, are hard to notice. Some visual indications, such as a change in the color of the sum, when the limit is exceeded, would be helpful.

As an improvement, we would like the text color of the the sum, and the status message, to be red when the sum exceeds the limit, and to be green when it is within the limit.

Let’s start off by giving an ID to the elements in which the sum and status message are displayed, so that they can be referenced from Tridash code. Surround <?@ sum ?> in a span element with ID sum and assign the div element, containing the status message, the ID status.

<div><strong>A + B = <span id="sum"><?@ sum ?></span></strong></div>
<div id="status">
  <?@
    case(
        sum < limit : "Within Limit.",
        "Limit Exceeded!"
    )
  ?>
</div>

Let’s create a node color which will be bound to the text color in which the sum and status message should be displayed. It should have the value "green" when the sum is within the limit and the value "red" when the sum exceeds the limit. This can be achieved by binding to a case functor node.

[Note]

The values "green" and "red" are strings, representing CSS color names.

Add the following to the Tridash code tag.

case(
  sum < limit : "green",
  "red"
) -> color

The value of the case functor node is "green" if sum is less than limit and "red" otherwise. The case functor node is bound to the color node.

The color node somehow has to be bound to the text color of the sum and status elements. Text color is a style attribute of an element. All style attributes are grouped under a single subnode style of the HTML element node. The text color is controlled by the color attribute, referenced using style.color.

The color node is bound to the style attributes of the elements with the following (add to the Tridash code tag):

color -> self.sum.style.color
color -> self.status.style.color

Full ui.html code:

ui.html. 

<?
 /import(core)

 a + b -> sum

 case (
     sum < limit : "green",
     "red"
 ) -> color

 color -> self.sum.style.color
 color -> self.status.style.color
?>
<!doctype html>
<html>
    <head>
        <title>Sum Limit</title>
    </head>
    <body>
      <h1>Sum Limit</h1>
      <div><label>Limit: <input value="<?@ to-int(limit) ?>"/></label></div>
      <hr/>
      <div><label>A: <input value="<?@ to-int(a) ?>"/></label></div>
      <div><label>B: <input value="<?@ to-int(b) ?>"/></label></div>
      <hr/>
      <div><strong>A + B = <span id="sum"><?@ sum ?></span></strong></div>
      <div id="status">
        <?@
          case(
              sum < limit : "Within Limit.",
              "Limit Exceeded!"
          )
        ?>
      </div>
    </body>
</html>

Build and run the application. Enter some values for a, b and the limit such that the sum exceeds the limit.

Limit: 10, A: 2, B: 9, A + B = 11 (red), Limit Exceeded! (red)

The status message and sum are now shown in red which provides an immediate visual indication that the limit has been exceeded.

Now increase the limit, or decrease the values of a and b:

Limit: 20, A: 2, B: 9, A + B = 11 (green), Within Limit. (green)

The color of the status message and sum is immediately changed to green, which provides a noticeable indication that the limit has no longer been exceeded.