
~~Title: Canvas Control API~~

<html><font color=#990000 size="+2"><b>Canvas Control API</b></font></html>

The <color #00a2e8>Knowledge Works™</color> is a powerful, <color #00a2e8>no-code</color> environment for buiding visually rich, browser applications. Users can create interactive, Real-Time and data-driven applications powered by StreamScape's //Data Fabric// and //Cognitive Analyics AI// services.  

//Canvas Controls// are individual elements of a //Component// that can be placed on the //canvas// area and configured to interact with the //Visual Component// or back-end query //Data Source//.  There are several categories of //Canvas Controls// that depend on their hosting container.  A core //Visual Component// such as a //Pie Chart// canvas may host the //Canvas Controls//. An [[IO Controls]] area, provided as a slide-out control area also host canvas //IO Controls//.  Finally, overla elements of a component (pop-ups) may host canvas //Popup Controls//.  See [[Canvas Controls]] for an extensive list of what is supported.

==== Control Life-Cycle Methods ====

Controls, such as //Buttons//, //Text Boxes//, //Icons//, //Combo Boxes//, //Panels// and similar elements exist within the Component container.  Depending on their function, controls may support several methods for managing life cyce and behavior. For example, data entry controls such as Text Input provide a way to check current values, reset state or disable input by making controls read-only.  Controls may also raise events that can be observed and processed by other controls within a component or even in different controls within a Scene.  

With multi-Scene support, controls can generate events that are processed by any component in an RTAI application.  As such, enabling, disaling controls, making them conditionaly //visible// or //hidden// is also part of their Life Cycle.

  *  ''control.reset()'':   Restores a specific control's value or checked state to it's default

  *  ''control.refresh()'': Updates and synchronizes controls content, merging //Shared Variable// content and visual changes.  Invokes the //init// trigger and assumes the control content's underlying data has changed. 

  *  ''control.render()'':  An alias for ''refresh()'' Used to render changes caused by user or ''reset()'' method.

  *  ''control.setDisabled(<boolean>)'':  Enables or disables a control.  Disabled controls will be visually inactive and will not accept user changes. 

\\

=== Control Value and State Checking ===

Individual controls also have options and methods that can be used to set and check their state and defaults.  Control changes are accumulated as result of user interactions and may be rendered at specific points in the application life cycle.  Components may also be automatically be initalized when they are created or the initialization may be deferred and triggered by a secific control action.

\\

  *  ''control.value'' Contins the current control value, allowing users to assign a new value or bind it to //data source// or linked to a __Shared Variable__

  *  control option ''isReload'' - if true, then the component will be reloaded after click/change and after trigger executed and the event raised.  (So you still can perform some logic in trigger/event handler of this control before reload will happen)

  *  control option ''isReset''  added - if true or array of control names, then all/specified controls will be reset after click/change of this control, trigger(if any) executed, event(if any) raised, and after component reloaded (if isReload is also set to try on this control)

  *  If the //Data Source// option ''Autoreload'' is enabled, the component will fetch data on load automaticaly.  User must call ''compoent.reload()'' explicitly to populate the //Data Model//.

  *  Control ''value'' and ''defaultValue'' may be statically declared in options, bound, or linked to a __Shared Variable__. 

  *  ''textInput.readOnly'' may set controls such as //Text//, a //Check Box// or a //Switch// and may be statically declared, bound, or linked to a __Shared Variable__.  

  *  ''isChecked'' property can be used to control //Check Box//, //Switch// or //Radio Buttons// state or configure a default. It may be statically declared, bound, or linked to a __Shared Variable__.

  *  Control API properties ''value'', ''isChecked'' and ''readOnly'' have corresponding methods ''setValue(<any>)'', ''setIsChecked(<boolean>)'' and ''setReadOnly(<boolean>)'' that set property values.  When the methods are called, ''control.render()'' is also called internally, to immediatley show changes.

  *  Control API methods ''isValueDefault'', ''isCheckedDefault'', and ''isReadOnlyDefault'' lets you check if a property is default or changed to determine if the user changed the control. 

  *  Calling ''control.reset()'' will return all control properties such as ''value'', ''isChecked'' and ''readOnly'' to their default settings

  *  Setting control value to ''undefined'' will reset the control property to it's default 

  * ''control.disabled = true'' or ''control.setDisabled(true)'' will disable the component and render it and it will ignoe any user interactions.  Users can specifu options like { semitransparent | desaturated | darkened } as effects on disabled controls.

  * ''control.isDisabledDefault()'' can check the satus of the control

\\

The Controls API provides ''control.options''for working with control properties, like color, size, values, etc. Each control has an ''options'' proxy object that holds current and changed values in its buffer. This provides read access to all existing control options, however changes to coponent options are written to the //delta buffer// and do not change the actual values, similar to how databases perform their operations until changes are commited.

When a canvas component is reloaded or rendered, the accumulated changes are merged and the new values are rendered. This reflects the changed state of chart-style components like //Graph// or //Bar Chart//. Control delta buffer changes, however can be managed in a very granular way using control API calls like ''render()'' or ''refresh()'' to allow for very specific behavior during //data fetch// or //data change//.

Note about //ComboBox// contol defaults: they select nothing if a specified ''defaultValue'' is not found in options list. If ''defaultValue'' is not set the first option is selected and displayed.

\\

=== Control Error Handling ===

Attempting to set non-existent options on a control will throw an exception.  For example:

<sxh DSQL; gutter: true;>

  // throws error same as in case of vanilla js objects
  console.log(control.options.mistyped.not.exists.option); 

</sxh>

\\

=== Getting and Setting Control Options ===

Individual //Control Options// such as color, font, size and so on, may be set, similar to the way //Component Optios// are set by using a specific API call. 

  * ''control.getOption(path, [defaultValue])'' gets the value of a control option and also return the default, if defined.

  * ''control.setOption(path, value)'' sets the value of a control option.

The ''path'' is expected to be relative to current the control's root.  See the <color #00a2e8>Knowledge Works™</color> component editor tool for specific paths.

\\

=== Show or Hide Controls ===

Controls may be rendered //visible// or //hidden// based on the actions of other components.  This can be useful in conditional form processing, Work Flow applications or in sutuations where content has to be conditionally reveaed to the user based on application interactions.  In this example a function is being called as part of Checkbox Trigger processing.  We lookup the panel and //show// its contents, making it visible based on a Checkbox value. The ''panel.render()'' will re-paint the contents of the component.  As such, multiple visual elements can be reveaed or hidden at once.   

<sxh DSQL; gutter: true;>

function(options, component, control, path, event) {   
  var panel = component.getControl('pnl_1');
  console.log(control.isChecked);
  panel.options.show = control.isChecked;
  panel.render();
}

</sxh>

\\

=== Group Controls ===

Certain controls such as //Radio Button//, //Check Box// and //Callout// can be grouped together to represent a single result value.  For such a case, the controls provide a ''group'' option that can organize the operations to be //mutually exclusive//. The //Component API// also provides several methods for working with groups.


    * ''component.getGroupSelectedValue('<group name>')'' gets the ''value'' of the selected control (if defined and selected) 

    * ''component.getGroupSelectedName('<group name>')'' gets the ''name'' of the selected control (useful if values is not set) 

    * ''component.setGroupSelectedValue('<group name>', value [, render=true])'' sets the ''value'' of selected component in a group.  If ''render'' is set to //true// the correspponding control in the group is refreshed as the selected one.

    * ''component.setGroupSelectedName('<group name>', value [, render=true])'' sets the ''name'' of selected component in a group.

Example:

<sxh DSQL; gutter: true;>

  var dayType = component.getGroupSelectedValue('DayCategory');
  if(dayType == undefined) dayType = 'none';

</sxh>

\\
==== Synchronous vs. Asynchronous Execution ====

The RTAI environment uses state-of-the-art [[wp>Reactive programming | Reactive Programming]] framework for asynchronous logic execution inside the browser. The so-called //Future/Promise framework// executes most of the code blocks in an asynchronous fashion while ensureing that Event Triggers fire //synchronously// as a consequence of a control action.  This allows the RTAI application components to seamlessly interact with the back-end //Event Fabric//, without impacting the browser. However, this behavior may have unexpected side-effects similar to other multi-threaded (non-preemptive) systems.  For example data fetch operations may not complete before users attempt to work with their results.  This can be especially pronounced in systems with slow networks.  For exaple, a chart or form may become available while their data is still being retreived.

In this case the scripting engine provides a mechanism to reverse this behavior and ensure order of operations, similar to how database transactions can group mutiple operations into a single unit of work.  The ''<color #7092be>async</color>'' directive instructs function execution to schedule asynchronousy, whereas ''<color #7092be>await</color>'' directive halts execution of the function block until he previous command returns.  This ensures synchronous execution of a logic block.

\\

<sxh DSQL; gutter: true;>

async function(options, component, control, path, event) {  
  await component.reload();
  console.log('loading completed..');
 }

</sxh>

\\

==== Chained Execution ====

In some cases it may be desirable to force a sequence of operations conditionally, to get around the asynchronous nature of the //Future/Promise framework//.  For example it may be necessary to execute a //data fetch// from the Data Fabric back-end and then load the results and refresh a coponent before returning ontrol to the user.  In this case, a ''<color #7092be>then(..)</color>'' function primitive can be used to force, so-called //chained execution//.

The example below illustrates how to write to the console log in a chained fashion, only after the ''component.reload()'' function completes.  Since a reload can trigger a lengthy data fetch, the log write will only occur after all function logic has completed.

\\

<sxh DSQL; gutter: true;>

function(options, component, control, path, event) {  
  component.reload().then(function(){
    console.log('loading completed');
  });
 }

</sxh>

\\

Alternativley, the ''<color #7092be>await</color>'' directive used idependantly can acheive smilar blocking results, but without conditional chaining.  In this case the ''component.refresh()'' logic will apply all changes at once and retun control after all elements of a component have rendered.

\\

<sxh DSQL; gutter: true;>

  // log the original value (ie. #FFFFFF)
  console.log(control.options.style.normal.color); 
  // override control options to make it red. 
  // all the changes saved separately from original.
  control.options.style.normal.color = '#FF0000'; 
  // now check to see if the change actually occured
  // keep in mind, some other condition coud have
  // triggered and prevented the change (this is for
  // example purposes only..)
  console.log(control.options.style.normal.color); 
  // now apply option changes.. we need to reload component, 
  // since control.render() will have no effect..
  await component.refresh(); 

</sxh>