TDIing out loud, ok SDIing as well

Ramblings on the paradigm-shift that is TDI.

Monday, November 21, 2016

JSON and XML Tutorial - Part 6, Making a custom Form

As promised, this post will walk you through creating a custom Connection Tab Form for the Connector scripted in the previous post. Myself being in true form, I will begin with a promised explanation and then a bit of theory.

Internal name of a component

In my last post I promised to show you how to discover the internal name of a component. Otherwise, how can you know that the HTTP Client Connector is loaded using this script call:

http = system.getConnector("ibmdi.HTTPClient");

There are a couple of routes to this knowledge. For one, you can ask the TDI Server for a report on installed components via the object menu (right-click) on your running Server.

Another approach is to select this component in the Inherit from setting of an AL Connector, Function or Connector Loop. Note that if you select a Library component found under the Resources of your project, then the internal name is not displayed. You must choose a basic component in order to get the internal name shown.

Bit o' theory: Internal names of parameters

Just as with components, every parameter in TDI (SDI) has an internal name which can be used to retrieve or set this value programmatically. To discover the internal name of a parameter, either click on the label for that paramter, or on the pencil button to the right of it.

This opens up the Parameter dialog where you can configure that the value is to be dynamically populated from a Property, a snippet of Javascript or a TDI/SDI Substitution expression. Near the top of this dialog box is the internal name of the parameter.

As noted above, the internal name of the parameter can be used to read and set its value, as in this example snippet bursting with helpful comments:

// 'thisConnector' is available in any Connector Hook and
// refers to the AL Connector owning the Hook.
// The property 'connector' references the Connector Interface
// selected for it via the Inherit From selector.
URLused = thisConnector.connector.getParam("url");

// Furthermore, all AL components are also script variables.

// If the name of a component is not suitable for a variable name,
// you can still access it by requesting a reference from the AL (i.e. task object).
ALconn = task.getConnector("My HTTP Connector");
ALconn.connector.setParam("url", "");

Another way to see the internal names of all the Parameters of a Connector is to attach it to a Connector Loop and then look at the Parameter Map tab.

This feature lets you map values to Connector settings as easy as mapping attributes. If Connector Loops are new to you, I suggest you read my other posts (or rather, 'love letters') on this handy feature. But I digress...

Back to creating a component Form

Returning to the topic for this post, we will now change the Connector scripted in the previous post so that the url used to make our REST request is entered as a parameter in the Connection tab of the Connector instead of being hardcoded.

The first thing we'll do is to close the editor for this Connector first. It is important that we do not open multiple editors for the same item, otherwise changes may be overwritten or lost. Once no editor is open for our Connector then we use the right-click option Open With... to edit it in the Forms Editor.

The first time you edit a component with the Forms Editor you are asked if you want to use the default form as a template. If you answer Yes for our example then you will get the default form for a Script Connector. Otherwise, the Forms Editor will start with an empty Form. For this example we'll answer Yes.

The Forms Editor screen is divided into three areas:

  1. The top pane where the name of the component and NLS resources (if any) are located.
  2. The navigator for the Form at the left.
  3. The details pane for any item selected in the navigator area.

In the top pane (1), the Form Title is entered. This value will be displayed at the top of the Connection tab. Unless you understand how translation resources are managed, leave the Translation File parameter unchanged or even empty. There are also two buttons located in this area: Test Form to launch the Form in a dialog for testing and for setting default values for parameters, and Delete Form.

The navigator area (2) provides an Init Script option for writing Javascript to be preloaded when this Form is opened in the CE (Connection tab), as well as an Events Script option for script that provides dynamic functionality to the Form. Neither of these will be discussed in this example, although the CouchDB Connector linked in a previous post uses this scripting option in the Form.

Immediately under these two script items are the Sections of the Form. Most standard components include a General and an Advanced section in the Connection tab. However, you can define as many sections as you need. Each section has a name (which is optional), the flag for whether this section is already expanded when the Connection tab is selected, and finally the list of Fields that are visible for this section. This list of Fields is ordered from top-down in the same way as they will appear in the Connection tab.

Below Sections is the list of Fields defined for the Form. Each field is identified by its internal name.

The details pane (3) provides the editor for whatever item is currently selected in the navigator. If the item selected is one of the two script options, then the details area will provide a script editor. If a Section or Field is selected then its editor appears in the details area.

For our example, we will start by creating the 'url' Field for our new Form by pressing the Add Field button at the bottom of the navigator pane and naming this Field 'url'.

In the Field Editor that appears to the right we can enter the Label for the field. This value appears onscreen to the left of the parameter setting in the Connection tab. We can also specify a ToolTip to be displayed on mouse hover-over. The Field type is chosen, which is String in this case. The Mode Selection option can be used to only display a field for certain modes by specifying these in a comma-separated list. We will leave this setting blank.

At the bottom of the editor are a series of tabs where one or more Buttons  to follow the parameter can be defined, drop-down values specified for Fields of a drop-down type, and where custom components and panel logic can be loaded.

If you want a bit more detail on most options available in the Forms editor, consult the TDI/SDI User Guide. We'll proceed now based on the description provided above. For example, this page for TDI 7.1.1 describes Forms Editor features, and is unchanged since TDI 7.1, as well as for later versions like SDI 7.2.

Now we select the General Section in the navigator and remove the $GLOBAL.script Field already visible here using the Remove Field button.  Then we add our new 'url' parameter by pressing the Add Field button at the bottom of the editor pane.

Now select the Advanced Section and remove the Fields already there. Finally, we add the $GLOBAL.script Field to this section.

Note that the $GLOBAL.* fields are predefined and provided access to standard features like file browsing and, in this case, script editing. Please note also that the $GLOBAL.debug Field represents the standard Detailed log checkbox found in most components. However, if you enable this for a Script component, then the entire script is written to the log. As a result, you will want to implement your own 'debug logging' Field if this functionality is desired.

Now if you press the Test Form button you should get a dialog with the Form you just defined.

Any values you enter for parameters here will become the default values for this component once you close and save the Form.

Now close and save the Forms Editor and use Open With... to open the Connector with the Connector Editor again. Now when you select the Connection tab then you will see your newly created Form.

Access field values in your script code

Our last step will be to edit the Connector script in order to have the 'url' variable set to the value entered for our REST URL parameter (internal name: url). We access the script using the Edit Script... button that we added to the Advanced section of the Form. We now need to decide how our component will behave regarding dynamic changes made to our parameter setting.

When the values entered in the Connection form of a component are actually used to refresh the settings of a Connector or Function differs from component to component, and in many cases, from parameter to parameter in a component. For example, the LDAP URL Parameter of the LDAP Connector is only refreshed when that component is initialized. However, the Search Filter parameter (internal name: ldapSearchFilter) is also refreshed before selectEntries() is performed for Iterator mode.

In our example here we will refresh the 'url' variable in the initialize() function only.

function initialize()
// Initialize the HTTP Client CI loaded above
// Reset the data set
nodeList = null;
// Refresh internal settings based on Form values
url = connector.getParam("url");


The predefined 'connector' variable provides access to our Connector Interface where all parameter values entered in the Form are kept.

Once we have made these changes to the script then our Connector is configurable via its Connection tab. Now we have a few options for sharing our new component.

Of course, the .connector file itself can be shared and dragged-dropped into new Projects. You can also use copy-paste directly in the CE to do this.

If we want to use our new REST Connector in multiple ALs in the same Project, we do so easily as long as our Connector is stored under Resources > Connectors.

If we want to leverage the component in another Project, then one way would be to define a Reference in this Project to the Config xml where the Connector is defined. Then it will be available for use in this other Project.

Another route would be to right-click on our Connector and choose the Publish... option. This allows us to export a 'package' to the packages folder of the installation directory. Components found in packages are available for use by any TDI Server started from this installation directory.

And until I get feedback warranting an update (as you may have seen me do for other posts) that'll be all for now.

No comments: