
~~Title: Component and Control API~~

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

The <color #00a2e8>Knowledge Works™</color> fabric application is an interactive, drag-and-drop environment for buiding visually rich, low-code applications. Users can create interactive, Real-Time and data-driven applications powered by StreamScape's Event Faric and Cognitive Analyics AI services.  Applications and 
visual components offer advanced navigation and user experience previously found only in native desktop applications; including modal dialogs, windows
and multi-dimensional navigation. 

==== Component Life-Cycle Methods ====

Components are self contained visual elements such as Charts, Forms or Applications that allow yout to interact with the Data Fabric back-end.
They can be deployed in a secure, stand-alone fashion as Quilt Desktop tiles, or as part of the RTAI Application Authoring environment.  A 
Component runs inside a container such as <color #00a2e8>Quilt.OS</color>, an <color #00a2e8>RTAI Scene</color> or embedded in a host 
web page as <color #00a2e8>MDI Frame</color> element. They rely on the host container for bootstrapping and provide a secure, robust API 
for controlling life cycle and behavior.

Components are comprised of visual chart elements such as //Pie Chart// or //Scatter Plot//, and //Visual Controls// like <color #00a2e8>IO Controls</color> 
in the <color #00a2e8>Control Tab</color> that can manage behavior of the component and <color #00a2e8>Canvas Controls</color> that can be 
placed directly on the surface of chart elements. Controls can //raise events// and execute business logic, triggered by user interaction such as
data entry, button clicks or state change.  Control triggers can also control the life cycle of components, instructing them to load data or render
visual elements.

  *  ''component.getVariable()'': Returns a //Shared Variable// value defined for this component.
  *  ''component.setVariable()'': Sets a //Shared Variable// value defined for this component.
  *  ''component.resetControls()'': Resets all controls and variables in a component to default state and flushes the data cache. Optionally users can specify an array of specific controls to reset as an argument.
  *  ''component.refresh()'': Updates and synchronizes all controls, their Shared Variable and renders visual changes.  Does not activate ''Spinner'' visual queue.
  *  ''component.reload()'': Fetches data from the back-end, completley reloads the //Data Model// and performs a ''refresh()''.  Activates the ''Spinner'' visual queue if enabled. The ''reload()'' function can also take //Data Source// elements as parameters and pass them as a literal or a JSON object.  This allows users to explicitly control how data are passed to the back-end function or query.
  *  ''component.show(.)'': Displays (opens) a compponent as an //Overelay Panel//, which is a light weight, in-browser window. Panel contents may need to be refreshed, reset or re-loaded prior to display as panels are //singleton// entities and initialized only once for optimization. Panel behavior, positon and vsialization can be controlled by passing a literal or a JSON object witht the following properties:

           * ''<color #7092be>horizontalPosition</color>'': Positions the overlay based on dimensions of browser area.  Possible settings are { 'center' | 'left' | 'right' }
           * ''<color #7092be>verticalPosition</color>'':   Positiond the overlay based on dimensions of browser are. Possible settings are { 'top' | 'bottom' | 'center'}
           * ''<color #7092be>padding</color>'': Screen layout padding, expressed a percentage.  For exapmle 11%.  Currently the only way to control Panel Dimensions.
           * ''<color #7092be>width</color>'': Absolute width of Panel in pixels.  For example 500px.  This is currently overriden by padding settings.
           * ''<color #7092be>height</color>'':  Absolute heigh of Panel in pixels.  For example 500px.  This is currently overriden by padding settings.
           * ''<color #7092be>closeButton</color>'': Display the close button in upper right corner, providing a Lightbox style view.
           * ''<color #7092be>mask</color>'' Controls shadow mask, where background is dimmed to emphasize the panel content. { 'true' | 'false' } where //true// means 0.3 opaqueness.  Or may be specificed as an opaque value 0-1.
           * ''<color #7092be>blur</color>'': Controls background blurring.  May be { true | false }
           * ''<color #7092be>desaturate</color>'': Controls color saturation of background.  May be { true | false }

   *  ''component.hide()'': Closes, but does not destroy the panel.
   *  ''component.setOption(.)'': Sets the option of a specific component to a value.  Options are dotted-notation path equivalents of //Component Editor// tree structures. The method take a path literal and a value as parameters.
   *  ''component.getOption(.)'': Sets the option of a specific component to a value.  Options are dotted-notation path equivalents of //Component Editor// tree structures.
   *  ''component.getControl()'': Returns a specific control that is part of this component.  Takes the name of a control as parameter. 
   *  ''component.getIcon(.)'': Returns a specific //Icon Resource// that has been registered with the //Component// or returns ''undefined'' if icon does not exist. 

   
=== Trigger Example: ===

<sxh DSQL; gutter: true;>

function(options, component, control, path, event) {  
    console.log('Resource to be removed: ' + component.getVariable('mRN'));
  
    if( (component.getVariable('mRN') == '') || (component.getVariable('accountId') == '') )  { 
        // Display error on the form'' canvas using a label, and return  
        component.setVariable('error_msg', 'Resource Name or Account is empty..');
        component.refresh();
        return;
        }
  
    // Attempt removal of Resource using account and name  ..log the Result. 
    component.reload( { accountId: component.getVariable('accountId'), mRN: component.getVariable('mRN'), remove: true } );
    
    component.refresh();
    var dt = component.data;
    console.log('Data Object: ', dt);
    ..
    }


</sxh>      

=== Scene Example: ===

<sxh DSQL; gutter: true;>

 crNavigator.addTrigger('cr_Navigator.btn_ok.Click', function(eventId, data) {
    // Trigger for an Event raised by component button click
    console.log('crNavigator Data: ', data);
    crClusteringTaskList.resetControls(['txt_accountId', 'txt_userId']);
    crClusteringTaskList.reload();
    ..
    ..
    // Access specific component Control  
    var c = aioRemoveMAccount.getControl('txt_accountId');
    aioRemoveMAccount.resetControls();
    aioRemoveMAccount.setVariable('accountId', data);
    c.readOnly = true;
    aioRemoveMAccount.reload();
    ..
});

</sxh>

=== Component Event Triggers ===

All RTAI Components support definition of //Event Triggers//.  These are peices of JavaScript logic that can execute conditionally based on //Canvas//, //Component// or //Application// events.  Currently the application level triggers are not supported.  However a wide range of // Canvas// and //Component// trigger actions can be defined.

=== One Time Triggers ===

The RTAI Component API also suports ''component.addOneTimeTrigger(..)'' method. It fires just like a normal trigger added via ''addTrigger(.)'' but it will only be exeuted once and then removed automatically.  This may be useful in situations where a single action needs to be taken on a component's initialization.

=== Working with Image Resources ===

Example button click trigger to illustrate //Icon Resource// API:

<sxh DSQL; gutter: true;>

function(options, component, control, path, event) {   
  // if we have icon with name 'star' - it's data url will be returned, undefined otherwise
  // console.log(component.getIcon('star')); 
  ..
  // read 'star' icon from library and assign it to button options.
  control.setOption('icon.url', component.getIcon('star')); 
  control.render();
}

</sxh>

=== Component Data Source Options ===

A component may have one or more //Data Source// queries defined that are used to fetch data and perform back-end operations.  Data sources
support several options that control component behavior and data caching.  Components can also be configured to serve their data competley from
//Data Cache// instead of fetching it fro the back-end.  Users can control cache loading behavior expicitly using a combinatyion of ''reload()''
 and ''refresh()''and other methods shown above.

   *  ''Live Control'': Instructs the component to ''autofetch'' data at an interval, specified in seconds. This employs non-blocking polling that can refresh the 
component's data in background, providing a smooth animated transition of values or visual elements.  The ''LED'' control can be added to a components canvas
to alert users when new data is fetched.  Fetching data every 10-15 seconds can be reasonable and will not impact the back-end system even if several hunred users are
accessing the same data concurrently.  At higher user volumes it may make sense to increase the polling period, cache contents that do not change often or deploy a 
cluster of nodes.

   *  ''Autoload'': Tells the control to fetch data immediatley when the user clicks a ''<color #7092be>Reload</color>''' button during development or when the component is first created. This is typically useful for charts or other visual elements that need to present data upon initialization. This setting can be turned off if the component needs to control it's own data fetch strategy.  In these cases, user must call ''compoent.reload()'' explicitly.


==== 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 coponent in an RTAI application.  As such, enabling, disaling controls, making them conditionaly //visible// or //hidden// is also part of their Life Cycle.

  *  ''control.reset()'': control API method added to restore current value/checked state to it's default
  *  ''control.render()''  this API method should be called to render changes caused by value/isChecked/reset()
  *  ''control.setDisabled()''  Enables or disabs control.  Disabled control will ve visually decorated o be inactive and will not accept user changes. 

=== Control Value and State Checking ===

Individual controls may also have options that can be used to check state and set state.   

  *  ''control.value'': API property added. It always has a current control value and you can also assign a new value - bound data source param will be auto-updated then.
  *  ''control.isChecked'': API property added, suitable for checkbox, switch, and radio button. if assigned - bound data source param will be auto-updated.

  *  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)
  *  Data Source opition ''Autoreload'' - if not enabled, the component will not fetch data on load automaticaly.  User must call ''compoent.reload()'' explicitly.

  *  Control ''value'' and ''defaultValue'' may be statically declared in options, bound, or linked to __Shared Variable__
  *  Control may be set as read-only using ''textInput.readOnly'' API method 
  *  ''isChecked'' and ''readOnly'' properties also work the same as ''value'' - if the value was changed, then it will be kept. If not changed or reset - then it will have default values as(if) specified in options, considering bindings/links
  *  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 called internally, to reflect changes visually.
  *  control API methods isValueDefault, isCheckedDefault, and isReadOnlyDefault added to check if the control has changed value or default one. This way you can check for example if the user changed this field or not. 
  *  Calling ''reset()'' will return all control API properties such as ''value'', ''isChecked'' and ''readOnly'' to their default settings
  *  Assigning ''undefined'' as control API property will reset a property individually to it's default 
  * ''control.disabled = true'' or ''control.setDisabled(true)'' will render a component grayed-out 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 RTAI 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 hange the actual values, similar to how databases perform their operations until changes are commited.

When a component is reloaded, the accumulated changes are merged and the new values are rendered. Delta buffer changes can be managed in a very granular way using specific AI calls like ''render(.)'' or ''refresh(.)'' to allow for very specific behavior during //data binding// and //data fetch//.

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>
 
=== 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>


==== 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 ''then(..)'' 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 ''await'' 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 oiginal 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>