The purpose of this document is to describe the series of component templates that form part of the UNIFACE Development Environment created by A J MARSTON. This development environment has two main parts:
It should be noted that the Menu and Security System was built using these component templates, and these templates assume that the finished components will be run under the same Menu and Security System. Some of the global procedures access the Menu database in order to obtain information on the current component such as initial values, field access restrictions, or optional runtime parameters.
All these are available for download from my web site on the 'Building Blocks' page.
In my old COBOL days there was a saying: "Nobody writes more than one program from scratch. All subsequent programs are simply modifications to the first". After a while you build up a collection of programs with different structures and/or behaviour patterns, therefore when building a new program it is easier to start with an existing one which has similar structure and behaviour - this means that there is more code that can be re-used and less code that has to be cut out.
This philosophy was carried forward when I started developing in UNIFACE. It quickly became apparent that a lot of components shared a common pattern of structure and behaviour, with the only difference being the content (data). It was quite common for a developer to be given a specification that simply said: "I want new program 'Y' to be just like program 'X', but with these database tables instead of those." The developer then took a copy of program 'X', named it 'Y', then went through it and manually changed all references to the entities and fields.
Initially program 'X' was just one of a group of programs that was spread over several different systems. Sometimes this meant that tracking down a program which could be copied took some time, so I decided to put them into a central place with meaningful names. This central place was a dummy application called TEMPLATE, and the programs were given names such as LIST1, LIST2, ADD1, UPDATE1, ENQUIRE1, DELETE1 and so on. The numbers help differentiate between minor variations in structure or behaviour.
All this was constructed in UNIFACE SIX. When UNIFACE SEVEN was released it contained a new object called a Component Template, which was a new and better way of creating components from predefined patterns. Because I already had my own collection of templates/patterns in a central place (the TEMPLATE application) it was a relatively simple exercise to make a Component Template from each of my forms, then rebuild each form using the Component Template. This proved to me that using Component Templates was far superior to the old-fashioned method of copy-and-rename. My next step was to repeat the exercise on a proper application which I was developing. Not many people would take the time to rebuild existing components, but I saw this as an investment that would pay for itself in the long run. I was not wrong. Whenever I needed to change the standard code in any of my components, either to take advantage of a change made by Compuware or to add a new facility of my own, it was a simple process to make the change once in the Component Template then recompile all the associated components so that they could automatically inherit the same change.
My original TEMPLATE application is now called XAMPLE, and is built into the sample software that is available for download from my web site on the 'Building Blocks' page. This provides working examples of each of my Component Templates so that the developer can see how each one is supposed to perform.
Component Templates are prefabricated, reusable pieces of software that enable the developer to create individual modules (components) within an application to a predefined model or pattern. They contain generic objects and default trigger code which help define the basic structure and behaviour of the component. To build a new component all that is required is to define the content or data (the entities and fields).
Each component template contains at least one generic entity, and each generic entity may optionally contain any number of generic fields. When a component is constructed from a template the first action is to go through an Object Binding screen so that these generic objects can be linked to 'real' objects. It is usual for each of these 'real' objects to have been pre-defined in the application model with any default trigger code.
Once the Object Binding screen has been successfully processed the component will be created with all the generic objects cross-referenced to the 'real' objects. All form-level triggers will be inherited from the template. All entity and field-level triggers will be inherited from the application model unless specifically overridden in the template. The developer is then free to complete the component by adding in any other entities, fields or trigger code as is required.
Building components from Component Templates offers the following advantages:-
It should be pointed out that inheritance from the template to a component is limited to the proc code contained within form-level triggers. Changes to entity-level or field-level triggers cannot be inherited. Changes in any generic objects cannot be inherited.
The component templates may contain a mixture of the generic entities which are listed below. Each of these generic entities may also contain a number of generic fields. In most cases only the first and last fields are defined on any generic entity so that their <previous field> or <next field> triggers can point to the correct field in the prompt sequence. Once the component has been created any number of other fields can be inserted between these two without affecting the prompt sequence.
Each form component has these generic objects arranged according to the standard layout shown in Figure 1. Note that not all of these objects appear in all forms.
Figure 1 - Standard Form Layout
Each form component will require the ACTION_BAR which contains a series of ACTION BUTTONS. These identify the actions that can be taken within the form (e.g. OK, Cancel, Retrieve, Clear). As the identity of each button is known in advance the name of each source button should also be used as the name of the target button. It is unlikely that any additional buttons should be required.
To simplify this process an ACTION_BAR entity has been created in the INF (infrastructure) model which contains all the required buttons. Objects in the component templates have been linked to this model and therefore do not need to be specified individually in the binding process.
Some form components include a NAVIGATION_BAR which contains a series of NAVIGATION BUTTONS. These cause other forms to be activated without having to return to a menu screen, and have the ability to pass the current context to the other form. As the identity of each button is not known in advance only two generic names are provided, called FIRST and LAST and which appear at either end of the bar. These buttons should either be given their real names (if known), or left with their generic names and renamed at a later stage. Additional buttons may be inserted anywhere between FIRST and LAST.
Each application should have its own copy of NAVIGATION_BAR (a non-database entity) defined in its application model. Each transaction (form component) which can be initiated from a navigation button should be defined as a field with its default trigger code so that it can simply be picked from the list during the binding process and will automatically inherit all the correct properties.
Some form components include the RETRIEVE_PROFILE entity which contains a series of fields used to specify selection criteria before retrieving entries from the database. Additional fields may be inserted anywhere between FIRST and LAST. There may also be a Full Profile button which will activate another form to allow more fields to be used as selection criteria.
Each application should have its own copy of RETRIEVE_PROFILE (a non-database entity) defined in its application model. Each field that will be used as a profile should be defined on this entity as an optional field so that it can be simply picked from the list during the binding process and will automatically inherit all the correct properties.
In forms of type LIST which contains multiple occurrences laid out in rows there should be a heading above each column which identifies that column. It is common practice for each column heading to be defined as a button which, when clicked, will cause the rows to be sorted by the value of that column, and to switch between ascending and descending sequence each time it is pressed.
A COLUMN_BAR entity has been defined in the INF model and therefore does not need to be defined within any other application model. During the binding process each column button should be given the same name as the field in that column. In the properties screen select File => Apply Field Template and choose COLUMN_BUTTON. This will set the default properties and trigger code for the button. The contents of the default <detail> trigger need not be amended unless the field to which it relates belongs to an entity other than the one defined with multiple occurrences. For example, if the field belongs to an inner entity then that entity name must be specified with the field name.
The Data Area contains objects which are specific to the application being developed. These are usually entities and fields which have already been defined in the application model.
Where a template is designed for a single entity the generic name is defined as "MAIN".
Where it deals with a ONE-to-MANY relationship then the names "ONE" and "MANY" (or "OUTER" and "INNER") are used.
Where a template deals with a ONE-to-MANY-to-ONE relationship the names "ONE_A", "MANY" and "ONE_B" (or "OUTER", "MANY" and "INNER") are used.
Field names will be shown either as "FIELD" where a single field is expected, or "FIRST" and "LAST" to indicate the first/last fields in the prompt sequence. Any number of other fields can be inserted between FIRST and LAST, including those on "up" entities if required.
|The component templates to not contain generic objects for every field that may be required in an actual component as the number of fields can vary enormously. The only fields that are defined as generic objects are the ones that are specifically referred to in the template's proc code. Some fields may require a dropdown list or a foreign entity, but that is considered to be part of the content of the component, and not the structure and behaviour which is predefined in the component template.|
|Where the FIRST or LAST field in a particular component does not actually exist on the entity in question you may be tempted to assign a dummy name in the Object Binding screen, then delete the field in the component as it cannot be renamed to a field on a different entity. If you delete the field then any references to its generic name in proc code (e.g.: for the prompt sequence) will not be satisfied and will have to be manually altered. To avoid this you can leave the dummy field on the form (or rename it to an actual but undisplayed field) but change it so that widget type = UNIFIELD, syntax = HIDDEN, and layout = NOTINV. Then change the <field gets focus> trigger to include a $prompt command to point to the correct field.|
Local constants are only copied from the template to the component when the component is first created. Subsequent changes within the template will have to be duplicated manually in every associated component.
Every component will contain a local constant named <FORM_VERSION> which starts with a predefined value of '01.000.000'. This is the initial version number for the component. The format of this number is AA.BBB.CCC where:-
|AA||is the MAJOR number, to be incremented whenever major changes are made to the object.|
|BBB||is the MINOR number, to be incremented whenever minor changes are made to the object.|
|CCC||is the BUG number, to be incremented whenever a bug is fixed.|
Whenever changes are made to the component this number should be incremented before it is released so that it contains the means to differentiate this version from other versions (refer to the component variable of the same name for details).
The advantage of using Local Constants for certain values is that although the actual value may change over a period of time (such as version number) or may change between different components, the constant name can be referenced in any number of places within the inherited code but changes to the constant's value do not require any changes to the inherited code. The constant's name is replaced by it's current value whenever the component is compiled.
Component variables are only copied from the template to the component when the component is first created. Subsequent changes within the template will have to be duplicated manually in every associated component.
The most commonly-used component variables are as follows:-
params/endparamsblock in the <exec> trigger in order to receive any parameters passed down by the parent form. This enables the contents of the parameter string to be available to other triggers within the component.
Some component variables can have their values set at run-time rather than compile-time. This allows the behaviour of the compnent to be altered without having to change or recompile any code. Refer to Menu and Security System User Guide, Appendix B - Extra Parameters for more details.
The single parameter string which is passed to the <exec> trigger is an associative list, therefore it may contain any number of id=value pairs. The standard code in the <exec> trigger contains the command
getlistitems/id/component $params$ which will examine the contents of the associative list and where an item name also exists as a component variable it will automatically set the value of the component variable to the value found in the list. This will make that value available to any trigger within the component.
All proc code which is contained within a component template, whether it be in a Form, Entity or Field trigger will be transferred to the component when it is first built from that component template.
Proc code which is built into any Form trigger in a component template has the added ability of inheritance. This means that code can be changed in a form trigger of the component template, and any existing components which were constructed from that component template need only be recompiled in order to inherit the same changes automatically - it is not necessary to manually make the same change in each of the existing components. This inheritance of trigger code between the component and its template only exists provided that the trigger code in the component is not modified in any way.
Most of the code in the component template will be defined in form-level triggers. In some circumstances there may be some standard local procs, but these will not be defined within the <local proc modules> trigger. This is because if any modifications are made to a form-level trigger (this includes the addition of a new local proc) then inheritance with the component template will be lost.
All local procs which should keep their ability to be inherited from the component template have therefore been defined in form-level triggers that should not usually be used (e.g.: <erase> or <retrieve sequential>). This leaves the developer free to use the <local proc modules> trigger as is necessary to satisfy the requirements of each individual component.
Code will only be defined in entity-level triggers for specific purposes. These will override any code defined in the application model. Once a component has been created from a template it is not possible to inherit any changes to any entity-level triggers from the template.
Generally speaking the only code defined in the component template is for the <previous field> and <next field> triggers, to define the prompt sequence. All other triggers will have their code inherited from the application model. Once a component has been created from a template it is not possible to inherit any changes to any field-level triggers from the template.