20.12 Passing data between the Client and Server

In most cases, information is passed between the Server and Client, and vice versa, in JSON format.  SRVROUTINEs can accept and return JSON data as:

  • a single field using a FIELD_MAP
  • a group of fields using a GROUP_MAP
  • a list of fields with multiple entries using a LIST_MAP.
  • The exception to the JSON formatting is when a Server Routine returns a Response Object. This is used to return the details for a single file.

    Some examples:

    Passing a Field or Group of Fields

    Passing a List of Data

    Returning a Response Object

    Passing a Field or Group of Fields

    The simplest way to pass data between the Client and Server, in either direction, is by mapping individual fields or groups of fields to the SRVROUTINE.  Values are passed to and from the SRVROUTINE based on position or using the optional PARAMETER_NAME value.

    It is important to note, when using a GROUP_MAP the associated group definition is not verified between the client and server. Fields in the group are mapped based on name.  Ensure you verify that the group definitions match, in terms of included fields, to ensure the expected data is passed and received as required.

    For example, the following code WILL FAIL when it attempts to update a record in the file xEmployee. Why? The Client GROUP_BY is only passing Identification, Surname and Give Names to the server, therefore the additional fields specified in the corresponding GROUP_BY on the Server Module will have no value. When the UPDATE is attempted it will fail to pass the validation rules.

    This can be rectified simply by ensuring the GROUP_BY definitions used for client and server processing contain the same fields. This may be a subset of the file fields or all the file fields depending on the transaction you are processing.

    The client Web Page or Reusable Part code may be something like this:

    Group_By Name(#MyEmployee) Fields(#xEmployeeIdentification #xEmployeeSurname #xEmployeeGivenNames)

     

    Mthroutine Name(UpdateEmployee)

     

    * Define the Server Module routine used to update data on the server

    Define_Com Class(#MyServerModule.UpdateEmployee) Name(#UpdateEmp)

     

    * Update the data asynchronously

    #UpdateEmp.ExecuteAsync Status(#io$sts) Employeedetails(#MyEmployee)

     

    Evtroutine Handling(#UpdateEmp.Completed)

     

    * Insert any processing after completion

     

    Endroutine

     

    Evtroutine Handling(#UpdateEmp.Failed) Handled(#handled)

     

    * Handle failure

     

    Endroutine

     

    Endroutine

    In the Server Module, the corresponding code might be something like this:

    Group_By Name(#Employee) Fields(#xEmployeeIdentification #xEmployeeSurname #xEmployeeGivenNames #xEmployeeStreet #xEmployeeCity #xEmployeeState #xEmployeePostalCode #xEmployeeCountry #xEmployeeHomeTelephone #xEmployeeBusinessTelephone #xEmployeeMobilePhone #xEmployeeSalary #xEmployeeStartDate #xEmployeeTerminationDate #xEmployeeDepartmentCode) 

     

    * Use this group definition to match the details sent from the Web Page

     

    * Group_By Name(#Employee) Fields(#xEmployeeIdentification #xEmployeeSurname #xEmployeeGivenNames)

     

    SrvRoutine Name(UpdateEmployee)

    Group_Map For(*input) Group(#Employee) Parameter_Name(EmployeeDetails)

    Field_Map For(*output) Field(#io$sts) Parameter_Name(Status)

     

    Update Fields(#Employee) In_File(xEmployee) With_Key (#xEmployeeIdentification)

     

    Endroutine

    Passing a List of Data

    A GROUP_MAP only includes a single instance of each field, to pass multiple instances of data a LIST_MAP can be used. 

    As with a GROUP_MAP, it is important ensure the DEF_LIST definitions on the client and server sides match, this includes the fields as well as the list size.

    This SRVROUTINE routine returns a list containing all the records in the file.

    Def_List Name(#Employees) Fields(#xEmployeeIdentification #xEmployeeSurname #xEmployeeGivenNames #xEmployeeStreet #xEmployeeCity #xEmployeeState #xEmployeePostalCode #xEmployeeCountry #xEmployeeHomeTelephone #xEmployeeBusinessTelephone #xEmployeeMobilePhone #xEmployeeSalary #xEmployeeStartDate #xEmployeeTerminationDate #xEmployeeDepartmentCode) Type(*working) Entrys(*MAX) 
    SrvRoutine Name(GetEmployees)
    List_Map For(*Output) List(#Employees)
    Select Fields(#Employees) From_File(xEmployee)
    Add_Entry To_List(#Employees)
    Endselect
    Endroutine

    There are two ways to receive the list on the Client side, the first is directly into a locally defined list, something like this:

    * Locally defined list

    Def_List Name(#EmployeeList) Fields(#xEmployeeIdentification #xEmployeeSurname #xEmployeeGivenNames #xEmployeeStreet #xEmployeeCity #xEmployeeState #xEmployeePostalCode #xEmployeeCountry #xEmployeeHomeTelephone #xEmployeeBusinessTelephone #xEmployeeMobilePhone #xEmployeeSalary #xEmployeeStartDate #xEmployeeTerminationDate #xEmployeeDepartmentCode) Type(*working) Entrys(*MAX) 

    Mthroutine Name(GetEmployees)

    * Define the Server Module method routine

    Define_Com Class(#MyServerModule.GetEmployees) Name(#GetEmps)

     

    * Return data into locally defined list

    #GetEmps.ExecuteAsync( #EmployeeList )

     

    Endroutine

    Alternately the data could be mapped directly into a data object collection by replacing the DEF_LIST with something like this:

    * Collection of Employee Items that acts as a list

    Define_Com Class(#Prim_acol<#MyEmployeeData>) Name(#EmployeeList)

    The associated reusable part, MyEmployeeData, would again need to have the corresponding fields included to ensure all the list data is available on the client.

    Function Options(*DIRECT)

    Begin_Com Role(*EXTENDS #PRIM_OBJT *listFields #MyEmployee)

    Group_By Name(#MyEmployee) Fields(#xEmployeeIdentification #xEmployeeSurname #xEmployeeGivenNames #xEmployeeStreet #xEmployeeCity #xEmployeeState #xEmployeePostalCode #xEmployeeCountry #xEmployeeHomeTelephone #xEmployeeBusinessTelephone #xEmployeeMobilePhone #xEmployeeSalary #xEmployeeStartDate #xEmployeeTerminationDate #xEmployeeDepartmentCode)

    End_Com

    Returning a Response Object

    A Response Object is typically used to download a file or image from the Application Server.

    In this example, an image is read from a database table. The response object is then populated with the image and the file name is set.

    SrvRoutine Name(DownloadImage) Response(#Response)
    Field_Map For(*Input) Field(#xEmployeeIdentification)
    Field_Map For(*output) Field(#io$sts) Parameter_Name(Status)
     
    Fetch Fields(#xEmployeeSurname #xEmployeeGivenNames) From_File(xEmployee) With_Key(#xEmployeeIdentification)
     
    If_Status Is(*okay)
     
    Fetch Fields(#empimg) From_File(xEmployeeImages) With_Key(#xEmployeeIdentification)
     
    If_Status Is(*okay)
     
    #Response.ContentFile := #Empimg.Filename
    #Response.AttachmentFileName := #xEmployeeSurname #xEmployeeGivenNames + (#xEmployeeIdentification
     
    Endif
     
    Endif
    Endroutine