UIZE JavaScript Framework

2012 NEWS 2012-09-15 - Improved Support for Conditions

Support for conditions in UIZE has been improved with the addition of support for compound conditions in the once instance method of the Uize.Class module.

With the improved once method, it is now easy to register code that should be executed once multiple state properties become truthy or falsy, or when a condition expression involving multiple properties evaluates to a truthy result.

EXAMPLE 1

myCollection.once (
  ['wired','!isEmpty'],
  function () {
    // execute code now that the collection widget is wired and no longer empty
  }
);

In the above example, code is being registered to be executed once the myCollection widget instance is wired and is not empty.

EXAMPLE 2

myFishTankWater.once (
  function (width,height,depth) {return width * height * depth > 1000},
  function () {
    // execute code, now that the water volume of the fish tank exceeds 1000
  }
}

In the above example, a compound condition is specified using a function. The arguments of the function - width, height, and depth - indicate that the condition is affected by the width, height, and depth state properties of the myFishTankWater instance. The function's body, return width * height * depth > 1000, evaluates the condition to be true when the volume of the fish tank's water is greater than 1000.

When code is registered to be executed once the product of the width, height, and depth properties is greater than 1000, if this condition is not yet met when the once method is called, the method will wire handlers for the Changed.width, Changed.height, and Changed.depth events and will re-evaluate the condition function every time any of the properties that affect the condition change value. Once the condition function returns a truthy result, the handler for the compound condition will be executed and the handlers that were wired for the Changed.* events will be unwired.

1. Full Documentation for the Improved once Instance Method

Lets you register a handler that should be executed only once a condition is met.

The once method is useful when using one or more state properties to form a condition, and where you wish to register code that should be executed once the condition has been met, and immediately if the condition is already met at the time that the once method is called.

DIFFERENT USAGES

Execute Code Once a State Property is Truthy or Falsy

wiringsOBJ = myInstance.once (propertyConditionSTR,handlerFUNC);

Execute Code Once Multiple State Properties Are Truthy or Falsy

wiringsOBJ = myInstance.once (propertiesConditionARRAYorSTR,handlerFUNC);

Execute Code Once a Compound Condition is Met

wiringsOBJ = myInstance.once (compoundConditionSTRorFUNC,handlerFUNC);

1.1. Execute Code Once a State Property is Truthy or Falsy

In its most basic usage, code can be registered to be executed once a single state property becomes truthy or falsy.

SYNTAX

wiringsOBJ = myInstance.once (propertyConditionSTR,handlerFUNC);

The propertyConditionSTR parameter specifies the name of a state property, with an optional "!" (exclamation mark) prefix for indicating condition inversion. If simply the name of a state property is specified, then the handler code specified by the handlerFUNC parameter will be executed once the property is truthy. If the optional "!" prefix is specified, then the handler code will be executed once the property is falsy.

EXAMPLE 1

myWidget.once (
  'wired',
  function () {
    // do something now that the widget has been wired
  }
);

In the above example, a handler is being registered to be executed once the widget myWidget has been wired (i.e. the value of its wired state property becomes true).

EXAMPLE 2

myCollectionWidget.once (
  '!isEmpty',
  function () {
    // do something now that the collection is no longer empty
  }
);

In the above example, code is being registered to execute once the isEmpty state property is false.

1.2. Execute Code Once Multiple State Properties Are Truthy or Falsy

Code can be registered to be executed once all properties in a set of state properties become truthy or falsy, by specifying the state properties as an array of property names or as a comma-separated list string.

SYNTAX

wiringsOBJ = myInstance.once (propertiesConditionARRAYorSTR,handlerFUNC);

1.2.1. propertiesConditionARRAYorSTR

The value specified for the propertiesConditionARRAYorSTR parameter may be an array of property names or a comma-separated list string.

Whichever form is used, any property name can be prefixed with a "!" (exclamation mark) to achieve condition inversion for the property.

Summary of different forms...

array of property names: ['phase1Done','phase2Done','phase3Done']
array of property names with condition inversion: ['wired','!isEmpty']
comma-separated list string: 'phase1Done, phase2Done, phase3Done'
comma-separated list with condition inversion: 'wired, !isEmpty'

1.2.1.1. Whitespace Ignored for Comma-separated List String

If a comma-separated list string is specified, all whitespace in the string is ignored.

This means that whitespace around the property names is ignored, so the value 'phase1Done,phase2Done,phase3Done' is equivalent to the value 'phase1Done, phase2Done , phase3Done'. This also means that whitespace around the optional "!" (exclamation mark) prefix is ignored, so the value 'wired, !isEmpty' is equivalent to the value 'wired, ! isEmpty'.

1.2.2. Examples

The following examples illustrate the different ways in which multiple properties can be specified.

1.2.2.1. EXAMPLE: Array of Property Names

Multiple state properties can be specified using an array of state property names.

EXAMPLE

myInstance.once (
  ['phase1Done','phase2Done','phase3Done'],
  function () {
    // execute code now that all three phases are done
  }
);

1.2.2.2. EXAMPLE: Comma-separated List String

Multiple state properties can be specified using a comma-separated list string.

EXAMPLE

myInstance.once (
  'phase1Done, phase2Done, phase3Done',
  function () {
    // execute code now that all three phases are done
  }
);

1.2.2.3. EXAMPLE: Array of Property Names, with Condition Inversion

Multiple state properties can be specified using an array of state property names, where some of the property names in the array are prefixed with the optional "!" to indicate condition inversion.

EXAMPLE

myCollection.once (
  ['wired','!isEmpty'],
  function () {
    // execute code now that the collection widget is wired and no longer empty
  }
);

1.2.2.4. EXAMPLE: Comma-separated List String, with Condition Inversion

Multiple state properties can be specified using a comma-separated list string, where some of the property names in the list are prefixed with the optional "!" to indicate condition inversion.

EXAMPLE

myCollection.once (
  'wired, !isEmpty',
  function () {
    // execute code now that the collection widget is wired and no longer empty
  }
);

1.3. Execute Code Once a Compound Condition is Met

Code can be registered to be executed once a compound condition is met, by specifying the compound condition in the form of a condition function or condition expression string.

SYNTAX

wiringsOBJ = myInstance.once (compoundConditionSTRorFUNC,handlerFUNC);

1.3.1. Condition Function

A compound condition can be specified as a function, where the names of the function's arguments indicate the state properties that affect the condition and where the function's body evaluates the condition.

EXAMPLE

myFishTankWater.once (
  function (width,height,depth) {return width * height * depth > 1000},
  function () {
    // execute code, now that the water volume of the fish tank exceeds 1000
  }
}

In the above example, a compound condition is specified using a function. The arguments of the function - width, height, and depth - indicate that the condition is affected by the width, height, and depth state properties of the myFishTankWater instance. The function's body, return width * height * depth > 1000, evaluates the condition to be true when the volume of the fish tank's water is greater than 1000.

When code is registered to be executed once the product of the width, height, and depth properties is greater than 1000, if this condition is not yet met when the once method is called, the method will wire handlers for the Changed.width, Changed.height, and Changed.depth events and will re-evaluate the condition function every time any of the properties that affect the condition change value. Once the condition function returns a truthy result, the handler for the compound condition will be executed and the handlers that were wired for the Changed.* events will be unwired.

1.3.2. Condition Expression String

A compound condition can be specified as an expression string, where the names of the state properties affecting the condition are specified along with an expression string for evaluating the condition.

A condition expression string is formatted with two parts separated by a ":" (colon) delimiter, where the part before the colon is a comma-separated list of the state properties affecting the condition, and the part after the colon is an expression to be used for evaluating the condition.

EXAMPLE

myFishTankWater.once (
  'width, height, depth : width * height * depth > 1000',
  function () {
    // execute code, now that the water volume of the fish tank exceeds 1000
  }
}

In the above example, a compound condition is specified using a condition expression string. In this string, the part before the colon - "width, height, depth" - indicates that the condition is affected by the width, height, and depth state properties of the myFishTankWater instance. The part after the colon - "width * height * depth > 1000" - evaluates the condition to be true when the volume of the fish tank's water (i.e. the product of the width, height, and depth properties) is greater than 1000.

1.4. Immediate Execution if Condition Already Met

If the condition specified in the call to the once method is already met at the time that the method is called, then the handler specified by the handlerFUNC parameter will be executed immediately.

Otherwise, handlers will be wired for the Changed.* (value change) events for all the state properties that affect the condition. The condition evaluator will be executed each time any of the watched properties change value. As soon as the condition becomes met (i.e. the condition evaluator produces a truthy result), the handlers wired to watch the value change events of the properties will be unwired and the handler function registered for the condition will be executed. By design, the handler is only executed for the first time that the condition becomes met.

1.5. Condition Inversion

As a convenience, the once method supports condition inversion through an optional "!" (logical not) prefix that can be placed before the condition name.

EXAMPLE

myCollectionWidget.once (
  '!isEmpty',
  function () {
    // do something now that the collection is no longer empty
  }
);

In the above example, code is being registered to execute once the isEmpty state property is false. This is done by prefixing the "isEmpty" condition name with a "!" (bang / exclamation) character to indicate that the code should execute only once the collection is not empty (i.e. the value of the isEmpty state property becomes false). The condition inversion facility is convenient in situations like this where you wish to execute code only once a property's value becomes falsy, rather than once the property's value becomes truthy (which is the standard behavior for the once method).

1.5.1. Condition Inversion with Multiple Property Conditions

Condition inversion can be used both with single state property conditions as well as multiple property conditions.

EXAMPLE

myCollectionWidget.once (
  ['wired','!isEmpty'],
  function () {
    // do something now that the collection is wired and no longer empty
  }
);

In the above example, code is being registered to be executed once the wired state property is truthy and the isEmpty state property is falsy. Condition inversion can also be used when the state properties are specified as a comma-separated list string, so specifying the condition as ['wired','!isEmpty'] is equivalent to specifying it as 'wired, !isEmpty'.

1.6. Wirings Object

The once method returns a wirings object that can be supplied to the unwire method in order to unwire the handler, in the unlikely event that one may wish to remove the handler before the condition becomes met.

This case is unlikely to arise except in exceptional situations, but the means is provided. In most cases, you will simply discard / ignore the return value of the once method. In the event that the condition is met when the once method is called, then the returned wirings object will be an empty object.

1.7. Handler Arguments

The handler code that is registered to be executed once a condition is met will be passed the values of all the state properties that affect the condition as arguments.

EXAMPLE

myFishTankWater.once (
  'width, height, depth : width * height * depth > 1000',
  function (width,height,depth) {
    alert (width + '(W) x ' + height + '(H) x ' + depth + '(D)');
  }
}

myFishTankWater.set ({
  width:10,
  height:11,
  depth:12
});

In the above example, code is being registered to be executed once the product of the width, height, and depth properties of the myFishTankWater instance exceeds 1000. Once the call to the set method has been executed, the volume of the fish tank's water will be 1320 and the handler will be executed.

Now, because the properties affecting the condition have been specified as "width, height, depth", the value of these state properties will be passed as arguments to the handler in the order width, height, and depth. In this case, the handler function is choosing to declare these function arguments, using the same names for the sake of clarity - you could ignore the arguments if you didn't care about the specific values at the time the condition is met, or you could use the arguments but name them differently. In this example, the alert statement will alert the text "10(W) x 11(H) x 12(D)".