Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EDT:Tutorial: RUI With DataBase Lesson 8"

 
(60 intermediate revisions by 5 users not shown)
Line 1: Line 1:
 +
[[EDT:Tutorial: Access a database with EGL Rich UI|Access a database with EGL Rich UI]]
  
[[EDT:Tutorial: Access a database with EGL Rich UI|Access a database with EGL Rich UI]]
+
<br>
  
 
+
{| class="FCK__ShowTableBorders" style="float: right;"
{| style="float: right"
+
|-
|[[EDT:Tutorial: RUI With DataBase Lesson 7|&lt; Previous]] | [[EDT:Tutorial: RUI With DataBase Lesson 9|Next >]]
+
| [[EDT:Tutorial: RUI With DataBase Lesson 7|&lt; Previous]] &#124; [[EDT:Tutorial: RUI With DataBase Lesson 9|Next &gt;]]
 
|}
 
|}
= Lesson 8: Add variables and functions to the Rich UI handler =
 
  
Add source code that supports the user interface.In lessons 8 and 9, you update the EGL source code directly
+
= Lesson 8: Add variables and functions to the Rich UI handler  =
and review changes in the Preview tab.
+
  
== Add code to support the data grid ==
+
Add source code that supports the user interface.In lessons 8 and 9, you update the EGL source code directly and review changes in the Preview tab.
  
Change the declaration of the data grid for two purposes: to
+
== Add code to support the data grid ==
cause the web page to react when the user selects a cell and to ensure
+
that the grid output is formatted correctly.
+
  
<ol><li>In Project Explorer, open '''PaymentClient''' &gt; '''EGLSource''' &gt; '''handlers''' and double-click PaymentFileMaintenance.egl.
+
Change the declaration of the data grid for two purposes:&nbsp;to cause the web page to react when the user selects a cell and to ensure that the grid output is formatted correctly.
<li>Click on the Source tab.
+
 
</ol>
+
#In Project Explorer, open '''PaymentClient''' &gt; '''EGLSource''' &gt; '''handlers''' and double-click '''PaymentFileMaintenance.egl'''.  
 +
#Click on the Source tab.
  
 
Make the following changes, ignoring the error marks:  
 
Make the following changes, ignoring the error marks:  
  
<ol><li>In the '''allPayments_ui''' DataGrid declaration,
+
In the '''allPayments_ui''' DataGrid declaration, add the following code immediately before the '''columns''' property:  
add the following code immediately before the '''columns''' property:  
+
  selectionListeners ::= cellClicked,
+
The '''selectionListeners''' property
+
specifies one or more functions that are called whenever the user
+
selects a cell in the grid. In this case, you are appending a function
+
name to a pre-existing array. You will write the '''cellClicked''' function
+
later in this lesson.
+
<li>Formatters are functions that change the appearance of
+
the values in DataGrid columns. To demonstrate the feature, find the
+
DataGridColumn declaration for category. To ensure that the user sees
+
a category description rather than an integer, add this code after '''width=90''':
+
  , formatters = [ formatCategory ]
+
<li>When you display dollar amounts in a column, you typically
+
right-align the values. You do not need to code a function to cause
+
right-alignment. Instead, add this code after the '''width''' entry
+
for amount:
+
  , alignment = DataGridLib.ALIGN_RIGHT
+
The '''allPayments_ui''' declaration
+
is now as follows, with error marks for '''cellClicked''' and '''formatCategory''':
+
      allPayments_ui DataGrid{
+
        layoutData = new GridLayoutData{
+
                          row = 2, column = 1,
+
                          verticalAlignment = GridLayoutLib.VALIGN_TOP},
+
        selectionListeners ::= cellClicked,
+
        columns =[
+
            new DataGridColumn{name = "category", displayName = "Type",
+
                              width = 90, formatters = [ formatCategory ]},
+
            new DataGridColumn{name = "description", displayName = "Description",
+
                              width = 120},
+
            new DataGridColumn{name = "amount", displayName = "Amount due",
+
                              width = 90, alignment = DataGridLib.ALIGN_RIGHT}
+
        ],
+
        data = allPayments as any[],
+
        selectionMode = DataGridLib.SINGLE_SELECTION};
+
<li>Save the file.
+
</ol>
+
  
== Code the function that responds when the user clicks the data
+
  selectionListeners&nbsp;::= cellClicked,
grid ==
+
  
 +
The '''selectionListeners''' property specifies one or more functions that are called whenever the user selects a cell in the grid. In this case, you are appending a function name to a pre-existing array. You will write the '''cellClicked''' function later in this lesson.
  
 +
Formatters are functions that change the appearance of the values in DataGrid columns. To demonstrate the feature, find the DataGridColumn declaration for category. To ensure that the user sees a category description rather than an integer, add this code after '''width=90''':
  
The '''cellClicked''' function is invoked when
+
  , formatters = [ formatCategory ]
the user clicks a cell in the data grid.
+
  
Immediately below the '''start''' function,
+
When you display dollar amounts in a column, you typically right-align the values. You do not need to code a function to cause right-alignment. Instead, add this code after the '''width''' entry for amount:  
add the following lines:
+
  
function cellClicked(myGrid DataGrid in)
+
   , alignment = DataGridLib.ALIGN_RIGHT
   selectedPayment = allPayments_ui.getSelection()[1] as paymentRec;
+
  selectedPayment_form.publish();
+
end
+
  
 +
The '''allPayments_ui''' declaration is now as follows, with error marks for '''cellClicked''' and '''formatCategory''':
  
First, the '''cellClicked''' function
+
      allPayments_ui DataGrid{
updates the '''selectedPayment''' record with data from
+
        layoutData = new GridLayoutData{
a single data-grid row. That row can include more fields than are
+
                        row = 2, column = 1,
displayed to the user. In this application, the single row in the
+
                        verticalAlignment = GridLayoutLib.VALIGN_TOP},
data grid will have come from a single row in the database.
+
        selectionListeners&nbsp;::= cellClicked,
 
+
        columns =[
Second,
+
          new DataGridColumn{name = "category", displayName = "Type",  
the '''publish''' function causes the transfer
+
                              width = 90, formatters = [ formatCategory ]},
of data from the '''selectedPayment''' record to the '''selectedPayment_ui''' layout.
+
          new DataGridColumn{name = "description", displayName = "Description",  
That transfer is made possible by code that was provided for you when
+
                              width = 120},
you created the '''selectedPayment_ui''' layout, which
+
          new DataGridColumn{name = "amount", displayName = "Amount due",  
is the single-record layout at the right of your web page. If you
+
                              width = 90, alignment = DataGridLib.ALIGN_RIGHT}
review the code, you can trace the relationships: 
+
        ],  
 
+
        data = allPayments as any[],
<ul><li>A Form Manager declaration includes form fields.
+
        selectionMode = DataGridLib.SINGLE_SELECTION};
<li>Each form field references a controller declaration.
+
<li>The controller declaration relates a model to a view; in this
+
case, a field of the '''selectedPayment''' record to a
+
child of the '''selectedPayment_ui''' layout.
+
</ul>
+
 
+
 
+
The Form Manager provides various benefits but is essentially
+
a collection of controllers.
+
 
+
Here is an explanation of two other
+
issues&quot;”the use of the bracketed array index ('''[1]'''),
+
and the use of the '''as''' operator:
+
 
+
<ul><li>The '''getSelection''' function always returns
+
a subset of the rows in the '''data''' array of
+
the data grid. However, when you declared the data grid, you specified
+
the following setting to indicate that the user can select only one
+
row at a time:  '''selectionMode = DataGridLib.SINGLE_SELECTION'''.
+
When the user can select only one row, only one element is available.
+
<li>Every element in the array returned by a '''getSelection''' function
+
is of type ANY. You typically use the same Record part to process
+
input to the grid and to process output from the grid, and in this
+
tutorial, the Record part is '''paymentRec'''. The Record
+
part has the following uses:
+
 
+
<ul><li>To be the basis of the array elements that you assign to the '''data''' property
+
of the data grid, as shown in the following setting:
+
 
+
data = allPayments as any[]
+
 
+
<li>To cast the array element that is returned by the '''getSelection''' function
+
of the data grid, as shown here:
+
 
+
allPayments_ui.getSelection()[1] as paymentRec
+
 
+
</ul>
+
 
+
In each case, the '''as''' clause provides
+
the necessary cast.
+
 
+
</ul>
+
 
+
  
 +
Save the file before going on to the next step.
  
== Format column values in the grid ==
+
== Code the function that responds when the user clicks the data grid ==
  
 +
The '''cellClicked''' function is invoked when the user clicks a cell in the data grid.
  
 +
Immediately below the '''start''' function, add the following lines:
  
To add the formatter function:
+
  function cellClicked(myGrid DataGrid in)
 +
    selectedPayment = allPayments_ui.getSelection()[1] as paymentRec;
 +
    selectedPayment_form.publish();
 +
  end
  
<ol><li>Add the following code before the final '''end''' statement
+
First, the '''cellClicked''' function updates the '''selectedPayment''' record with data from a single data-grid row. That row can include more fields than are displayed to the user. In this application, the single row in the data grid will have come from a single row in the database.
in the file:
+
  
function formatCategory(class string, value string, rowData any in)
+
Second, the '''publish''' function causes the transfer of data from the '''selectedPayment''' record to the '''selectedPayment_ui''' layout. That transfer is made possible by code that was provided for you when you created the '''selectedPayment_ui''' layout, which is the single-record layout at the right of your web page. If you review the code, you can trace the relationships:
  value = PaymentLib.getCategoryDesc(value as INT);
+
end
+
  
Formatters have the parameters shown. In this case,
+
*A Form Manager declaration includes form fields.  
the formatter wraps a library function you created earlier.
+
*Each form field references a controller declaration.
 +
*The controller declaration relates a model to a view; in this case, a field of the '''selectedPayment''' record to a child of the '''selectedPayment_ui''' layout.
  
<li>Press Ctrl-Shift-O to organize the required import statements
+
The Form Manager provides various benefits but is essentially a collection of controllers.  
and save the file. All the error marks disappear.
+
</ol>
+
  
== Test the formatting of the data grid and the transfer of data
+
Here is an explanation of two other issues: the use of the bracketed array index ('''[1]'''), and the use of the '''as''' operator:
to the single-record layout ==
+
  
 +
#The '''getSelection''' function always returns a subset of the rows in the '''data''' array of the data grid. However, when you declared the data grid, you specified the following setting to indicate that the user can select only one row at a time: '''selectionMode = DataGridLib.SINGLE_SELECTION'''. When the user can select only one row, only one element is available.
 +
#Every element in the array returned by a '''getSelection''' function is of type ANY. You typically use the same Record&nbsp;type to process input to the grid and to process output from the grid, and in this tutorial, the Record&nbsp;type is '''paymentRec'''. The Record&nbsp;type has the following uses:
  
 +
:*To be the basis of the array elements that you assign to the '''data''' property of the data grid, as shown in the following setting:
  
You can test your recent changes even before you gain
+
  data = allPayments as any[]
access to the database.
+
  
 +
:*To cast the array element that is returned by the '''getSelection''' function of the data grid, as shown here:
  
<ol><li>Click the Preview tab and note that the categories are
+
  allPayments_ui.getSelection()[1] as paymentRec
now descriptions (for example, &quot;œRent&quot;? rather than &quot;œ1&quot;?). [[Image:EDT_Tutorial_edt_richui_sql08_formatted_grid.jpg|The formatted data grid]]
+
  
<li>Click one or another row in the data grid and note that
+
In each case, the '''as''' clause provides the necessary cast.
the single-row layout is updated appropriately. However, the formatter
+
affected only the data grid, and the description field in the single-row
+
layout contains a numeric. The tutorial will address that issue later.
+
<li>Click the Source tab and change the '''start''' function
+
so that the first record in the prototype data includes a value for '''payeeName''',
+
which is a '''paymentRec''' record field that is not displayed
+
by the data grid: 
+
  
function start()
+
== Format column values in the grid  ==
  allPayments_ui.data =[
+
      new paymentRec{
+
        category = 1, description = "test01", amount = 100.00
+
        ''' , payeeName = "Someone" '''},
+
      new paymentRec{category = 2, description = "test02", amount = 200.00},
+
      new paymentRec{category = 3, description = "test03", amount = 300.00}];
+
    end
+
  
<li>Click the Preview tab and click the first row in the data
+
To add the formatter function:  
grid. [[Image:EDT_Tutorial_edt_richui_sql08_prototype_and_all_plus.jpg|The data transferred from data grid to the single-record layout]]
+
  
</ol>
+
*Add the following code before the final '''end''' statement in the file:
As shown, you can switch quickly from one tab in the Rich
+
UI editor to another, to test even a small change.
+
  
== Comment the prototype data ==
+
  function formatCategory(class_1 string inout, value string inout, rowData any in)
 +
    value = PaymentLib.getCategoryDesc(value as INT);
 +
  end
  
You can comment or uncomment code quickly, as shown in this
+
:Formatters have the parameters shown. In this case, the formatter wraps a library function you created earlier.
step.
+
  
<ol><li>Click the Source tab.
+
*Press Ctrl-Shift-O to organize the required import statements and save the file. All the error marks disappear.
<li>In the '''start''' function, select the complete
+
assignment statement, right-click the area selected, and click Comment. [[Image:EDT_Tutorial_edt_richui_sql08_comment.jpg|The selection of code and the Comment menu item]]
+
  
<li>Comment marks (//) are now at the start of each line. You
+
== Test the formatting of the data grid and the transfer of data to the single-record layout ==
could remove the comments by repeating the task and clicking '''Uncomment''' instead
+
of '''Comment'''. However, leave the comments in
+
place. EGL also supports the use of slash asterisk (/*)
+
and asterisk slash (*/) delimiters, as shown here:
+
  
/*
+
You can test your recent changes even before you gain access to the database.  
+
    You can add comments in either of two ways.
+
+
*/
+
  
</ol>
+
*Click the Preview tab and note that&nbsp;the categories are now descriptions (for example, '''Rent''' rather than '''1'''). [[Image:EDT Tutorial edt richui sql08 formatted grid.jpg|The formatted data grid]]
 +
*Click one or another row in the data grid and note that the single-row layout is updated appropriately. However, the formatter affected only the data grid, and the description field in the single-row layout contains a numeric. The tutorial will address that issue later.
 +
*Click the Source tab and change the '''start''' function so that the first record in the prototype data includes a value for '''payeeName''', which is a '''paymentRec''' record field that is not displayed by the data grid:
  
== Declare a service-access variable ==
+
  function start()
 +
    allPayments_ui.data =[
 +
    new paymentRec{
 +
        category = 1, description = "test01", amount = 100.00
 +
        ''' , payeeName = "Someone" '''},
 +
    new paymentRec{category = 2, description = "test02", amount = 200.00},
 +
    new paymentRec{category = 3, description = "test03", amount = 300.00}];
 +
  end
  
 +
*Click the Preview tab and click the first row in the data grid. [[Image:EDT Tutorial edt richui sql08 prototype and all plus.jpg|The data transferred from data grid to the single-record layout]]
  
 +
As shown, you can switch quickly from one tab in the Rich UI editor to another, to test even a small change.
  
You now declare a service-access variable, which will
+
== Comment the prototype data  ==
let you communicate with the service that you defined earlier.
+
  
 +
You can comment or uncomment code quickly, as shown in this step.
  
To create the variable:
+
*Click the Source tab.
 +
*In the '''start''' function, select the complete assignment statement, right-click the area selected, and click '''EGLSource &gt; Toggle Comment'''. [[Image:EDT Tutorial edt richui sql08 toggle comments.jpg|The selection of code and the Comment menu item]]
 +
*Comment marks (//) are now at the start of each line. You could remove the comments by selecting the commented statements and repeating the task. However, leave the comments in place. EGL also supports the use of slash asterisk (/*) and asterisk slash (*/) delimiters, as shown here:
  
<ol><li>Near the top of the EGL source code, find the handler declaration
+
  /*
for '''PaymentFileMaintenance'''. Add a blank line, and
+
      You can add comments in either of two ways.  
immediately before the '''ui''' GridLayout declaration,
+
  */
add the following statement:
+
  
dbService SQLService{@dedicatedService};
+
== Declare a service-access variable  ==
  
The '''@dedicatedService''' property
+
'''Note''':&nbsp; The mechanism for service access has changed during the last weeks.&nbsp; The detail shown in the current and subsequent sections of this tutorial will work for older versions of EDT.&nbsp; However, to learn about the current mechanism, which came after version 0.81 Milestone 2, see the following page:&nbsp; [[EDT:Resource Binding Services|Service bindings]] .<br><br>You now declare a service-access variable, which will let you communicate with the service that you defined earlier. EDT uses the term "dedicated service" to refer to a service deployed on the same server as the Rich UI Hander. You do not need to specify any location information for services designated as dedicated service.  
indicates that the service being referenced is a dedicated service,
+
which will be deployed with the Rich UI handler.  
+
  
In the following
+
To create the variable for calling a dedicated service:  
display, the red X in the margin indicates a problem in the code:  
+
[[Image:EDT_Tutorial_edt_richui_sql08_red_x.jpg|The declaration of the service-access variable, in code]]
+
  
To see the error message,
+
*Near the top of the EGL source code, find the handler declaration for '''PaymentFileMaintenance'''. Add a blank line, and immediately before the '''ui''' GridLayout declaration, add the services variables.
move the cursor over the X.
+
  
<li>Fix the &quot;œunresolved type&quot;? error by pressing Ctrl+Shift+O. The new '''import''' statement provides
+
*On EDT 0.7.0, declare the variable as follows:
access to the '''services''' package, '''SQLService''' part,
+
which is in the '''PaymentService''' project. The
+
reference to '''SQLService''' is resolved because that
+
project is on the EGL build path of the '''PaymentClient''' project.
+
<li>Save the file.
+
</ol>
+
  
== Create functions that use the service-access variable to invoke
+
  dbService SQLService?{@dedicatedService};
the service ==
+
  
You now create several functions to invoke different functions
+
:The '''@dedicatedService''' property indicates that the service being referenced is a dedicated service.
in the dedicated service. Once you understand how to set up one invocation,
+
the others are straightforward.
+
  
Begin by creating the function that reads all data.
+
*On EDT 0.8.0, the '''dedicatedService''' property is dropped. Instead you code a service binding in addition to the services variable:
  
<ol><li>Leave a blank line after the '''cellClicked''' function
+
  dbService SQLService?&nbsp;;
and add the following code:
+
dedicatedServiceBinding HTTPProxy&nbsp;;
  
function readFromTable()
+
:The '''HTTPProxy''' type indicates to the handler that it should call back to the server from which it was loaded to invoke any service called with that binding variable.
  call dbService.getAllPayments() returning to updateAll
+
      onException serviceLib.serviceExceptionHandler;
+
end
+
  
'''Note:'''
+
*When you defined the service variable, the editor will show a red X in the margin indicating a problem in the code, as shown in the following display from EDT 0.7.0: [[Image:EDT Tutorial edt richui sql08 red x.jpg|The declaration of the service-access variable, in code]]
  
<ol><li>The '''call''' statement in Rich UI is a variation
+
:To see the error message, move the cursor over the X.
used only to access services. The runtime communication in this case
+
is asynchronous, which means that the user can continue to interact
+
with the handler while the service is responding.
+
<li>The asynchronous '''call''' statement includes
+
two function names:
+
  
<ul><li>'''updateAll'''
+
*Fix the '''unresolved type''' error by pressing Ctrl+Shift+O. The new '''import''' statement provides access to the '''services''' package, '''SQLService''' type, which is in the '''PaymentService''' project. The reference to '''SQLService''' is resolved because that project is on the EGL build path of the '''PaymentClient''' project.
<li>'''serviceLib.serviceExceptionHandler'''
+
*Save the file.
</ul>
+
  
The two are ''callback functions'', which are invoked
+
== Create functions that use the service-access variable to invoke the service ==
by the EGL runtime code after the service responds or fails. If the
+
service returns a value successfully, the '''updateAll''' function
+
is invoked. If the call fails, the EGL runtime code invokes a function
+
that is associated with the name '''serviceLib.serviceExceptionHandler'''.
+
  
By
+
You now create several functions to invoke different functions in the dedicated service. Once you understand how to set up one invocation, the others are straightforward.  
default, an error results in the display of error information in the
+
Console view (at development time) or at the bottom of the web page
+
(at run time). However, you can specify an error handler of your own,
+
typically by assigning a function name in place of '''serviceLib.serviceExceptionHandler'''.
+
  
</ol>
+
First add a service exception handler prior to the final '''end''' statement. Include the following code:
  
<li>Click anywhere in the '''call''' statement,
+
  function serviceExceptionHandler(ex anyException)
right-click, and click '''Create Callback Functions'''.
+
    sysLib.writeStderr("Failure: " + ex.message);
Alternatively, you could clicked anywhere in the statement, held down
+
    if (ex isa ServiceInvocationException)
the Ctrl key, and pressed 1. [[Image:EDT_Tutorial_edt_richui_sql08_callback.jpg|The Create Callback Functions option]]
+
      sysLib.writeStderr("Detail 1: " +
EGL creates an empty '''updateAll''' function.
+
          (ex as ServiceInvocationException).detail1);
An error handler would have been created as well if you had specified
+
      sysLib.writeStderr("Detail 2:  " +
a function name for '''onException''', other than '''serviceLib.serviceExceptionHandler'''.
+
          (ex as ServiceInvocationException).detail2);
 +
      sysLib.writeStderr("Detail 3:  " +
 +
          (ex as ServiceInvocationException).detail3);
 +
    end
 +
  end
  
The
+
Then include the function that reads all data from the table. Leave a blank line after the ''cellClicked''' function. Then add the following code if you are using EDT 0.7.0:  
parameter list in the created '''updateAll''' function
+
is equivalent to the type of return value that is expected from the
+
service. Here are the relationships that explain the behavior of the
+
Rich UI editor: 
+
  
<ul><li>The parameter list in the callback function is correct because
+
  function readFromTable()
the '''getAllPayments''' function in the Service part is
+
    call dbService.getAllPayments() returning to updateAll
available to the editor.
+
        onException serviceExceptionHandler;
<li>The function is available because you resolved the reference to
+
  end
the '''SQLService''' part in a previous step.
+
</ul>
+
  
+
If you are using EDT 0.8.0, you must add a '''using ''binding-variable'' ''' clause to this call statement (and all call statements that invoke services):
</ol>
+
  
Next, create the function that adds sample data.
+
  function readFromTable()
 +
    call dbService.getAllPayments()
 +
        using dedicatedServiceBinding
 +
        returning to updateAll
 +
        onException serviceExceptionHandler;
 +
  end
  
<ol><li>Click Ctrl-F to gain access to the Find/Replace dialog,
+
'''Notes:'''  
type '''SampleData''', and click '''Find'''.
+
<li>Update the '''sampleData''' function so that
+
the code is as follows:
+
  
function sampleData(event Event in)
+
#The '''call''' statement in Rich UI is a variation used only to access services. The runtime communication in this case is asynchronous, which means that the user can continue to interact with the handler while the service is responding.
  call dbService.createDefaultTable() returning to updateAll
+
#The 0.8.0 '''using''' clause specifies the variable name of the '''HTTPProxy''' variable you specified earlier in the program. The variable type of '''HTTPProxy''' indicates the service is called as a dedicated service.  
      onException serviceLib.serviceExceptionHandler;
+
#The asynchronous '''call''' statement includes two function names:
end
+
#*'''updateAll'''
 +
#*'''serviceExceptionHandler'''
  
You do not use the '''Create Callback Functions''' feature
+
The two are ''callback functions'', which are invoked by the EGL runtime code after the service responds or fails. If the service returns a value successfully, the '''updateAll''' function is invoked. If the call fails, the EGL runtime code invokes the function named '''serviceExceptionHandler'''.  
because the callback functions exist.  
+
  
</ol>
+
The example exception handler writes text to the Eclipse console or other standard error output. This behavior is appropriate for prototypes and early in development. For production code, you need to consider the exception-handling requirements that are specific to your situation. <br>  
  
Next, create the function that adds data.
+
Now, to create the '''updateAll''' callback function, click anywhere in the '''call''' statement, hold down the Ctrl key, and press 1.<br>[[Image:EDT Tutorial edt richui sql08 ctl-1 callback.jpg|The Create Callback Functions option]]<br>EGL offers you an empty '''updateAll''' function. Click on '''Create function''' in the pop-up window to have the empty function inserted into your program. An error handler would have been created as well if you had not already included a function named '''serviceExceptionHandler''' in the program.  
  
<ol><li>Update the '''addRow''' function so that the
+
The parameter list in the created '''updateAll''' function is equivalent to the type of return value that is expected from the service. Here are the relationships that explain the behavior of the Rich UI editor:  
code is as follows:  
+
  
function addRow(event Event in)
+
*The parameter list in the callback function is correct because the '''getAllPayments''' function in the Service&nbsp;type is available to the editor.  
  call dbService.addPayment(new paymentRec) returning to recordAdded
+
*The function is available because you resolved the reference to the '''SQLService'''&nbsp;type in a previous step.
      onException serviceLib.serviceExceptionHandler;
+
end
+
  
<li>Click anywhere in the '''call''' statement,
+
Next, create the function that adds sample data. Click Ctrl-F to gain access to the Find/Replace dialog, type '''SampleData''', and click '''Find'''. Update the '''sampleData''' function so that the code is as follows for Version 0.7.0:
right-click, and click '''Create Callback Functions'''. EGL adds the '''recordAdded''' function.
+
</ol>
+
  
Create the function that deletes data.
+
  function sampleData(event Event in)
 +
    call dbService.createDefaultTable() returning to updateAll
 +
        onException serviceExceptionHandler;
 +
  end
  
<ol><li>Update the '''deleteRow''' function so that
+
For Version 0.8.0, code:  
the code is as follows:  
+
  
function deleteRow(event Event in)
+
  function sampleData(event Event in)
 +
    call dbService.createDefaultTable()
 +
        using dedicatedServiceBinding
 +
        returning to updateAll
 +
        onException serviceExceptionHandler;
 +
  end
  
  for(i INT from 1 to allPayments.getSize())
+
You do not use the feature to create the callback function because the callback functions already exist.  
      if(allPayments[i].paymentID == selectedPayment.paymentID)
+
        allPayments.removeElement(i);
+
        exit for;
+
      end
+
  end
+
  
  call dbService.deletePayment(selectedPayment) returning to recordRevised
+
Next, create the function that adds data. Update the '''addRow''' function so that the code is as follows for Version 0.7.0:
      onException serviceLib.serviceExceptionHandler;
+
end
+
  
The function acts as follows:
+
  function addRow(event Event in)
 +
    call dbService.addPayment(new paymentRec) returning to recordAdded
 +
        onException serviceExceptionHandler;
 +
  end
  
<ul><li>Deletes the selected row from the local array of records
+
For Version 0.8.0 add the clause "using dedicatedServiceBinding" to the call statement.
<li>Calls the database service to delete the row from the database
+
itself
+
</ul>
+
  
<li>Click anywhere in the '''call''' statement,
+
Now click anywhere in the '''call''' statement and press Ctrl-1 to create an empty call back function. Click on '''Create function''' in the pop-up window to included the '''recordAdded''' function in your program.  
right-click, and click '''Create Callback Functions'''. EGL adds the '''recordRevised''' function.
+
<li>Press Ctrl-Shift-F to format the code.
+
<li>Save the file.
+
</ol>
+
''' Related information '''<br>
+
[../../com.ibm.egl.pg.doc/topics/pegl_ui_richui_rest_call_statement.html Invoking a service asynchronously from a Rich UI application]
+
  
== Update the start function to initialize the data grid with
+
Now create the function that deletes data. Update the '''deleteRow''' function so that the code is as follows for Version 0.7.0:
database rows ==
+
  
 +
  function deleteRow(event Event in)
 +
    for(i INT from 1 to allPayments.getSize())
 +
        if (allPayments[i].paymentID == selectedPayment.paymentID)
 +
          allPayments.removeElement(i);
 +
          exit for;
 +
        end
 +
    end
 +
    call dbService.deletePayment(selectedPayment) returning to recordRevised
 +
        onException  serviceExceptionHandler;
 +
  end
  
 +
For Version 0.8.0 add the clause "using dedicatedServiceBinding" to the call statement.
  
To initialize the data grid, add the following code before
+
The function acts as follows:  
the '''end''' statement of the '''start''' function:
+
  
 +
*Deletes the selected row from the local array of records
 +
*Calls the database service to delete the row from the database itself
  
 +
Now click anywhere in the '''call''' statement and press Ctrl-1 to create an empty call back function. Click on '''Create function''' in the pop-up window to included the '''recordRevised''' function in your program.
  
readFromTable();
+
To complete this step,
  
 +
*Press Ctrl-Shift-F to format the code.
 +
*Save the file.
  
Although
+
'''Related information '''<br>
you could have assigned the '''readFromTable''' function
+
directly to the '''onConstructionFunction''' property,
+
you are advised to retain the '''start''' function as a
+
separate unit of logic in case you later decide to add other code
+
that runs before the web page is rendered.
+
  
Retain the commented
+
*Help topic: Invoking a service asynchronously from a Rich UI application<br>
code in the '''start''' function in case you need to test
+
the web page without accessing the database. You can use the comment
+
and uncomment capability of the Rich UI editor to quickly switch from
+
the function call to the prototype data and back again.
+
  
 +
== Update the start function to initialize the data grid with database rows  ==
  
== Complete the callback functions ==
+
To initialize the data grid, add the following code before the '''end''' statement of the '''start''' function:
  
You now complete the callback functions that were created
+
  readFromTable();
automatically:
+
  
<ul><li>'''updateAll'''
+
Although you could have assigned the '''readFromTable''' function directly to the '''onConstructionFunction''' property, you are advised to retain the '''start''' function as a separate unit of logic in case you later decide to add other code that runs before the web page is rendered.
<li>'''recordAdded'''
+
<li>'''recordRevised'''
+
</ul>
+
  
 +
Retain the commented code in the '''start''' function in case you need to test the web page without accessing the database. You can use the comment and uncomment capability of the Rich UI editor to quickly switch from the function call to the prototype data and back again.
  
The '''updateAll''' function receives an array
+
== Complete the callback functions  ==
of '''paymentRec''' records from the dedicated service.
+
The function is called in the following ways:
+
  
<ul><li>As a callback function at startup, after the '''readFromTable''' function
+
You now complete the callback functions that were created automatically:
calls the service.
+
<li>As a callback function whenever the user clicks the '''Sample''' button
+
to invoke the '''sampleData''' function.
+
</ul>
+
  
<ol><li>Update the '''updateAll''' function so that
+
*'''updateAll'''  
the code is as follows:
+
*'''recordAdded'''
 +
*'''recordRevised'''
  
function updateAll(retResult paymentRec[] in)
+
The '''updateAll''' function receives an array of '''paymentRec''' records from the dedicated service. The function is called in the following ways:
  allPayments = retResult;
+
  allPayments_ui.data = allPayments as any[];
+
end
+
  The function updates the global
+
array of payment records with the data received from the service and
+
then refreshes the data grid.
+
</ol>
+
  
The '''recordAdded''' function receives the
+
*As a callback function at startup, after the '''readFromTable''' function calls the service.
record that was sent to and returned by the service function '''addPayment'''.
+
*As a callback function whenever the user clicks the '''Sample''' button to invoke the '''sampleData''' function.
  
<ol><li>Update the '''recordAdded''' function so that
+
Update the '''updateAll''' function so that the code is as follows:  
the code is as follows:  
+
  
function recordAdded(newPayment paymentRec in)
+
  function updateAll(retResult paymentRec[] in)
  readFromTable();
+
    allPayments = retResult;
end
+
    allPayments_ui.data = allPayments as any[];
The function '''readFromTable''' reads
+
  end
all the rows from the database. The data stored by the grid can then
+
contain the new row, including the '''paymentID''' value
+
that was automatically generated by the database and that is otherwise
+
unavailable to the grid.
+
</ol>
+
  
The '''recordRevised''' function receives
+
The function updates the global array of payment records with the data received from the service and then refreshes the data grid.  
the record that was sent to and returned by the service function '''addPayment'''.
+
  
<ol><li>Update the '''recordRevised''' function so that
+
The '''recordAdded''' function receives the record that was sent to and returned by the service function '''addPayment'''. Update the '''recordAdded''' function so that the code is as follows:  
the code is as follows:  
+
  
function recordRevised(delPayment paymentRec in)
+
  function recordAdded()
  allPayments_ui.data = allPayments as any[];
+
    readFromTable();
end
+
  end
The function refreshes the data grid.
+
<li>Press Ctrl-Shift-F to format the code. If you see errors
+
in your source file, compare your code to the file contents in [[EDT:Tutorial: RUI With DataBase Lesson 8 Code|Code for PaymentLib.egl after lesson 8]].
+
<li>Save the file.
+
</ol>
+
  
== Test the interface ==
+
The function '''readFromTable''' reads all the rows from the database. The data stored by the grid can then contain the new row, including the '''paymentID''' value that was automatically generated by the database and that is otherwise unavailable to the grid.
  
Preview your work now that you are accessing a database.
+
The '''recordRevised''' function receives the record that was sent to and returned by the service function '''addPayment'''.Update the '''recordRevised''' function so that the code is as follows:
  
<ol><li>Click the Preview tab. The data grid has
+
  function recordRevised()
no content because you commented out the prototype data, and the database
+
    allPayments_ui.data = allPayments as any[];
has no rows.[[Image:EDT_Tutorial_edt_richui_sql08_initial_ui.jpg|The initial data grid]]
+
  end
  
<li>Click '''Sample''' to create sample data.  
+
The function refreshes the data grid.  
<li>If EGL requests a password, enter '''admin''' for
+
both the '''User ID''' and '''Password''' fields.
+
Select '''Remember my user ID...''' and click '''OK'''. [[Image:EDT_Tutorial_edt_richui_sql08_pwd.jpg|The ID and password window]]
+
  
If you exit and restart the workbench
+
Clean up this step by formatting your code and saving the file. If you see errors in your source file, compare your code to the file contents in [[EDT:Tutorial: RUI With DataBase Lesson 8 Code|Code for&nbsp;PaymentFileMaintenance.egl after lesson 8]].
before you complete this tutorial, this window might be re-displayed
+
the next time you attempt to access the database.
+
Eventually
+
the grid is re-displayed with rows of sample data.[[Image:EDT_Tutorial_edt_richui_sql08_sample_data.jpg|Three payment entries, &quot;œApartment,&quot;? &quot;œGroceries,&quot;? and &quot;œISP&quot;? are displayed]]
+
  
<li>Click the '''Add''' button. A new row with a single default value is displayed at
+
== Test the interface ==
the bottom of the grid. [[Image:EDT_Tutorial_edt_richui_sql08_added.jpg|A new row is added]]
+
  
<li>Select the Apartment row and click '''Delete'''. The row is deleted from both the display and the database.[[Image:EDT_Tutorial_edt_richui_sql08_deleted.jpg|The &#34;Apartment&#34; row is now gone.]]
+
Before your test, add the SQL database binding to the deployment descriptor for the '''PaymentClient''' project just as you did for the '''ServiceProject''' deployment descriptor as described in the section [[EDT:Tutorial: RUI With DataBase Lesson 6#Create_binding_to_database_connection|Create binding to database connection]], then let's preview your work which is to access a database.
  
<li>Click the first row of the data grid. [[Image:EDT_Tutorial_edt_richui_sql08_transfer_database.jpg|The data grid and single-record layout both contain data from the database.]]
+
#Click the Preview tab. The data grid has no content because you commented out the prototype data, and the database has no rows.<br>[[Image:EDT Tutorial edt richui sql08 initial ui.jpg|The initial data grid]]
  Data from the database was transferred
+
#Click '''Sample''' to create sample data.
from the data grid to the single-record layout. Note that the value
+
#Click the '''Add''' button. A new row with a single default value is displayed at the bottom of the grid.<br>[[Image:EDT Tutorial edt richui sql08 added.jpg|A new row is added]]
of the '''Key''' field reflects how many rows were
+
#Select the Apartment row and click '''Delete'''. The row is deleted from both the display and the database.<br>[[Image:EDT Tutorial edt richui sql08 deleted.jpg|The "Apartment" row is now gone.]]
added to the database and will probably not match the value on your
+
#Click the first row of the data grid.<br>[[Image:EDT Tutorial edt richui sql08 transfer database.jpg|The data grid and single-record layout both contain data from the database.]] Data from the database was transferred from the data grid to the single-record layout. Note that the value of the '''Key''' field reflects how many rows were added to the database and will probably not match the value on your web page.
web page.
+
</ol>
+
  
== Lesson checkpoint ==
+
== Lesson checkpoint ==
  
You learned how to complete the following tasks:
+
You learned how to complete the following tasks:  
  
<ul><li>To create formatters.
+
*To create formatters.  
<li>To respond to the user's selection in a data grid.
+
*To respond to the user's selection in a data grid.  
<li>To transfer data from the data grid to a grid layout.
+
*To transfer data from the data grid to a grid layout.  
<li>To comment and uncomment code.
+
*To comment and uncomment code.  
<li>To access services from a Rich UI application.
+
*To access services from a Rich UI application.
</ul>
+
  
In the next lesson, you will complete the code for the Rich
+
In the next lesson, you will complete the code for the Rich UI handler.  
UI handler.
+
  
{| style="float: right"
+
{| class="FCK__ShowTableBorders" style="float: right;"
|[[EDT:Tutorial: RUI With DataBase Lesson 7|&lt; Previous]] | [[EDT:Tutorial: RUI With DataBase Lesson 9|Next >]]
+
|-
 +
| [[EDT:Tutorial: RUI With DataBase Lesson 7|&lt; Previous]] &#124; [[EDT:Tutorial: RUI With DataBase Lesson 9|Next &gt;]]
 
|}
 
|}
  
 
[[Category:EDT]]
 
[[Category:EDT]]

Latest revision as of 04:12, 14 December 2012

Access a database with EGL Rich UI


< Previous | Next >

Lesson 8: Add variables and functions to the Rich UI handler

Add source code that supports the user interface.In lessons 8 and 9, you update the EGL source code directly and review changes in the Preview tab.

Add code to support the data grid

Change the declaration of the data grid for two purposes: to cause the web page to react when the user selects a cell and to ensure that the grid output is formatted correctly.

  1. In Project Explorer, open PaymentClient > EGLSource > handlers and double-click PaymentFileMaintenance.egl.
  2. Click on the Source tab.

Make the following changes, ignoring the error marks:

In the allPayments_ui DataGrid declaration, add the following code immediately before the columns property:

  selectionListeners ::= cellClicked, 

The selectionListeners property specifies one or more functions that are called whenever the user selects a cell in the grid. In this case, you are appending a function name to a pre-existing array. You will write the cellClicked function later in this lesson.

Formatters are functions that change the appearance of the values in DataGrid columns. To demonstrate the feature, find the DataGridColumn declaration for category. To ensure that the user sees a category description rather than an integer, add this code after width=90:

  , formatters = [ formatCategory ]

When you display dollar amounts in a column, you typically right-align the values. You do not need to code a function to cause right-alignment. Instead, add this code after the width entry for amount:

  , alignment = DataGridLib.ALIGN_RIGHT

The allPayments_ui declaration is now as follows, with error marks for cellClicked and formatCategory:

     allPayments_ui DataGrid{
       layoutData = new GridLayoutData{
                        row = 2, column = 1,
                        verticalAlignment = GridLayoutLib.VALIGN_TOP},
       selectionListeners ::= cellClicked,
       columns =[
          new DataGridColumn{name = "category", displayName = "Type", 
                             width = 90, formatters = [ formatCategory ]},
          new DataGridColumn{name = "description", displayName = "Description", 
                             width = 120},
          new DataGridColumn{name = "amount", displayName = "Amount due", 
                             width = 90, alignment = DataGridLib.ALIGN_RIGHT}
       ], 
       data = allPayments as any[],
       selectionMode = DataGridLib.SINGLE_SELECTION};

Save the file before going on to the next step.

Code the function that responds when the user clicks the data grid

The cellClicked function is invoked when the user clicks a cell in the data grid.

Immediately below the start function, add the following lines:

  function cellClicked(myGrid DataGrid in)
    selectedPayment = allPayments_ui.getSelection()[1] as paymentRec;
    selectedPayment_form.publish();
 end

First, the cellClicked function updates the selectedPayment record with data from a single data-grid row. That row can include more fields than are displayed to the user. In this application, the single row in the data grid will have come from a single row in the database.

Second, the publish function causes the transfer of data from the selectedPayment record to the selectedPayment_ui layout. That transfer is made possible by code that was provided for you when you created the selectedPayment_ui layout, which is the single-record layout at the right of your web page. If you review the code, you can trace the relationships:

  • A Form Manager declaration includes form fields.
  • Each form field references a controller declaration.
  • The controller declaration relates a model to a view; in this case, a field of the selectedPayment record to a child of the selectedPayment_ui layout.

The Form Manager provides various benefits but is essentially a collection of controllers.

Here is an explanation of two other issues: the use of the bracketed array index ([1]), and the use of the as operator:

  1. The getSelection function always returns a subset of the rows in the data array of the data grid. However, when you declared the data grid, you specified the following setting to indicate that the user can select only one row at a time: selectionMode = DataGridLib.SINGLE_SELECTION. When the user can select only one row, only one element is available.
  2. Every element in the array returned by a getSelection function is of type ANY. You typically use the same Record type to process input to the grid and to process output from the grid, and in this tutorial, the Record type is paymentRec. The Record type has the following uses:
  • To be the basis of the array elements that you assign to the data property of the data grid, as shown in the following setting:
  data = allPayments as any[]
  • To cast the array element that is returned by the getSelection function of the data grid, as shown here:
  allPayments_ui.getSelection()[1] as paymentRec 

In each case, the as clause provides the necessary cast.

Format column values in the grid

To add the formatter function:

  • Add the following code before the final end statement in the file:
  function formatCategory(class_1 string inout, value string inout, rowData any in)
    value = PaymentLib.getCategoryDesc(value as INT);
 end
Formatters have the parameters shown. In this case, the formatter wraps a library function you created earlier.
  • Press Ctrl-Shift-O to organize the required import statements and save the file. All the error marks disappear.

Test the formatting of the data grid and the transfer of data to the single-record layout

You can test your recent changes even before you gain access to the database.

  • Click the Preview tab and note that the categories are now descriptions (for example, Rent rather than 1). The formatted data grid
  • Click one or another row in the data grid and note that the single-row layout is updated appropriately. However, the formatter affected only the data grid, and the description field in the single-row layout contains a numeric. The tutorial will address that issue later.
  • Click the Source tab and change the start function so that the first record in the prototype data includes a value for payeeName, which is a paymentRec record field that is not displayed by the data grid:
  function start()
    allPayments_ui.data =[
    new paymentRec{
       category = 1, description = "test01", amount = 100.00
        , payeeName = "Someone" },
    new paymentRec{category = 2, description = "test02", amount = 200.00},
    new paymentRec{category = 3, description = "test03", amount = 300.00}];
 end
  • Click the Preview tab and click the first row in the data grid. The data transferred from data grid to the single-record layout

As shown, you can switch quickly from one tab in the Rich UI editor to another, to test even a small change.

Comment the prototype data

You can comment or uncomment code quickly, as shown in this step.

  • Click the Source tab.
  • In the start function, select the complete assignment statement, right-click the area selected, and click EGLSource > Toggle Comment. The selection of code and the Comment menu item
  • Comment marks (//) are now at the start of each line. You could remove the comments by selecting the commented statements and repeating the task. However, leave the comments in place. EGL also supports the use of slash asterisk (/*) and asterisk slash (*/) delimiters, as shown here:
  /* 
     You can add comments in either of two ways. 
 */

Declare a service-access variable

Note:  The mechanism for service access has changed during the last weeks.  The detail shown in the current and subsequent sections of this tutorial will work for older versions of EDT.  However, to learn about the current mechanism, which came after version 0.81 Milestone 2, see the following page:  Service bindings .

You now declare a service-access variable, which will let you communicate with the service that you defined earlier. EDT uses the term "dedicated service" to refer to a service deployed on the same server as the Rich UI Hander. You do not need to specify any location information for services designated as dedicated service.

To create the variable for calling a dedicated service:

  • Near the top of the EGL source code, find the handler declaration for PaymentFileMaintenance. Add a blank line, and immediately before the ui GridLayout declaration, add the services variables.
  • On EDT 0.7.0, declare the variable as follows:
  dbService SQLService?{@dedicatedService};
The @dedicatedService property indicates that the service being referenced is a dedicated service.
  • On EDT 0.8.0, the dedicatedService property is dropped. Instead you code a service binding in addition to the services variable:
  dbService SQLService? ;
dedicatedServiceBinding HTTPProxy ;
The HTTPProxy type indicates to the handler that it should call back to the server from which it was loaded to invoke any service called with that binding variable.
  • When you defined the service variable, the editor will show a red X in the margin indicating a problem in the code, as shown in the following display from EDT 0.7.0: The declaration of the service-access variable, in code
To see the error message, move the cursor over the X.
  • Fix the unresolved type error by pressing Ctrl+Shift+O. The new import statement provides access to the services package, SQLService type, which is in the PaymentService project. The reference to SQLService is resolved because that project is on the EGL build path of the PaymentClient project.
  • Save the file.

Create functions that use the service-access variable to invoke the service

You now create several functions to invoke different functions in the dedicated service. Once you understand how to set up one invocation, the others are straightforward.

First add a service exception handler prior to the final end statement. Include the following code:

  function serviceExceptionHandler(ex anyException)
    sysLib.writeStderr("Failure: " + ex.message);
    if (ex isa ServiceInvocationException)
      	sysLib.writeStderr("Detail 1:  " + 
      	    (ex as ServiceInvocationException).detail1);
      	sysLib.writeStderr("Detail 2:  " + 
      	    (ex as ServiceInvocationException).detail2);
      	sysLib.writeStderr("Detail 3:  " + 
      	    (ex as ServiceInvocationException).detail3);
    end
 end

Then include the function that reads all data from the table. Leave a blank line after the cellClicked' function. Then add the following code if you are using EDT 0.7.0:

  function readFromTable()
    call dbService.getAllPayments() returning to updateAll
       onException serviceExceptionHandler;
 end

If you are using EDT 0.8.0, you must add a using binding-variable clause to this call statement (and all call statements that invoke services):

  function readFromTable()
    call dbService.getAllPayments() 
       using dedicatedServiceBinding
       returning to updateAll
       onException serviceExceptionHandler;
 end

Notes:

  1. The call statement in Rich UI is a variation used only to access services. The runtime communication in this case is asynchronous, which means that the user can continue to interact with the handler while the service is responding.
  2. The 0.8.0 using clause specifies the variable name of the HTTPProxy variable you specified earlier in the program. The variable type of HTTPProxy indicates the service is called as a dedicated service.
  3. The asynchronous call statement includes two function names:
    • updateAll
    • serviceExceptionHandler

The two are callback functions, which are invoked by the EGL runtime code after the service responds or fails. If the service returns a value successfully, the updateAll function is invoked. If the call fails, the EGL runtime code invokes the function named serviceExceptionHandler.

The example exception handler writes text to the Eclipse console or other standard error output. This behavior is appropriate for prototypes and early in development. For production code, you need to consider the exception-handling requirements that are specific to your situation.

Now, to create the updateAll callback function, click anywhere in the call statement, hold down the Ctrl key, and press 1.
The Create Callback Functions option
EGL offers you an empty updateAll function. Click on Create function in the pop-up window to have the empty function inserted into your program. An error handler would have been created as well if you had not already included a function named serviceExceptionHandler in the program.

The parameter list in the created updateAll function is equivalent to the type of return value that is expected from the service. Here are the relationships that explain the behavior of the Rich UI editor:

  • The parameter list in the callback function is correct because the getAllPayments function in the Service type is available to the editor.
  • The function is available because you resolved the reference to the SQLService type in a previous step.

Next, create the function that adds sample data. Click Ctrl-F to gain access to the Find/Replace dialog, type SampleData, and click Find. Update the sampleData function so that the code is as follows for Version 0.7.0:

  function sampleData(event Event in)
    call dbService.createDefaultTable() returning to updateAll
       onException serviceExceptionHandler;
 end

For Version 0.8.0, code:

  function sampleData(event Event in)
    call dbService.createDefaultTable() 
       using dedicatedServiceBinding
       returning to updateAll
       onException serviceExceptionHandler;
 end

You do not use the feature to create the callback function because the callback functions already exist.

Next, create the function that adds data. Update the addRow function so that the code is as follows for Version 0.7.0:

  function addRow(event Event in)
    call dbService.addPayment(new paymentRec) returning to recordAdded
       onException serviceExceptionHandler;
 end

For Version 0.8.0 add the clause "using dedicatedServiceBinding" to the call statement.

Now click anywhere in the call statement and press Ctrl-1 to create an empty call back function. Click on Create function in the pop-up window to included the recordAdded function in your program.

Now create the function that deletes data. Update the deleteRow function so that the code is as follows for Version 0.7.0:

  function deleteRow(event Event in)
    for(i INT from 1 to allPayments.getSize()) 
       if (allPayments[i].paymentID == selectedPayment.paymentID)
          allPayments.removeElement(i);
          exit for;
       end
    end
    call dbService.deletePayment(selectedPayment) returning to recordRevised
       onException  serviceExceptionHandler;
 end

For Version 0.8.0 add the clause "using dedicatedServiceBinding" to the call statement.

The function acts as follows:

  • Deletes the selected row from the local array of records
  • Calls the database service to delete the row from the database itself

Now click anywhere in the call statement and press Ctrl-1 to create an empty call back function. Click on Create function in the pop-up window to included the recordRevised function in your program.

To complete this step,

  • Press Ctrl-Shift-F to format the code.
  • Save the file.

Related information

  • Help topic: Invoking a service asynchronously from a Rich UI application

Update the start function to initialize the data grid with database rows

To initialize the data grid, add the following code before the end statement of the start function:

 readFromTable();

Although you could have assigned the readFromTable function directly to the onConstructionFunction property, you are advised to retain the start function as a separate unit of logic in case you later decide to add other code that runs before the web page is rendered.

Retain the commented code in the start function in case you need to test the web page without accessing the database. You can use the comment and uncomment capability of the Rich UI editor to quickly switch from the function call to the prototype data and back again.

Complete the callback functions

You now complete the callback functions that were created automatically:

  • updateAll
  • recordAdded
  • recordRevised

The updateAll function receives an array of paymentRec records from the dedicated service. The function is called in the following ways:

  • As a callback function at startup, after the readFromTable function calls the service.
  • As a callback function whenever the user clicks the Sample button to invoke the sampleData function.

Update the updateAll function so that the code is as follows:

  function updateAll(retResult paymentRec[] in)
    allPayments = retResult;
    allPayments_ui.data = allPayments as any[];
 end

The function updates the global array of payment records with the data received from the service and then refreshes the data grid.

The recordAdded function receives the record that was sent to and returned by the service function addPayment. Update the recordAdded function so that the code is as follows:

  function recordAdded()
    readFromTable();
 end

The function readFromTable reads all the rows from the database. The data stored by the grid can then contain the new row, including the paymentID value that was automatically generated by the database and that is otherwise unavailable to the grid.

The recordRevised function receives the record that was sent to and returned by the service function addPayment.Update the recordRevised function so that the code is as follows:

  function recordRevised()
    allPayments_ui.data = allPayments as any[];
 end

The function refreshes the data grid.

Clean up this step by formatting your code and saving the file. If you see errors in your source file, compare your code to the file contents in Code for PaymentFileMaintenance.egl after lesson 8.

Test the interface

Before your test, add the SQL database binding to the deployment descriptor for the PaymentClient project just as you did for the ServiceProject deployment descriptor as described in the section Create binding to database connection, then let's preview your work which is to access a database.

  1. Click the Preview tab. The data grid has no content because you commented out the prototype data, and the database has no rows.
    The initial data grid
  2. Click Sample to create sample data.
  3. Click the Add button. A new row with a single default value is displayed at the bottom of the grid.
    A new row is added
  4. Select the Apartment row and click Delete. The row is deleted from both the display and the database.
    The "Apartment" row is now gone.
  5. Click the first row of the data grid.
    The data grid and single-record layout both contain data from the database. Data from the database was transferred from the data grid to the single-record layout. Note that the value of the Key field reflects how many rows were added to the database and will probably not match the value on your web page.

Lesson checkpoint

You learned how to complete the following tasks:

  • To create formatters.
  • To respond to the user's selection in a data grid.
  • To transfer data from the data grid to a grid layout.
  • To comment and uncomment code.
  • To access services from a Rich UI application.

In the next lesson, you will complete the code for the Rich UI handler.

< Previous | Next >

Back to the top