Mobile RPG Tutorial

In this tutorial you will develop a Mobile RPG application from start to finish. Along the way you will learn some Mobile RPG concepts and be encouraged to further explore other Mobile RPG Documentation and development materials to learn more about the different components and their options.

Prerequisites

To complete this tutorial you must have a Windows 7, 8, or 10 development machine with:

  1. Microsoft's Visual Studio 2017
  2. ASNA Mobile RPG, including all samples and documentation.

You also need access to an IBM i server with:

  1. Rational Open Access: RPG Edition
  2. ASNA DataGate for IBM i
  3. ASNA Mobile RPG Handler
  4. All ASNA Mobile RPG samples and files restored to their proper libraries and IFS locations.

The tutorial assumes you have a working knowledge of RPG programming on the IBM i, including creation of data files. The data files the tutorial uses are part of the samples that ship with Mobile RPG and their creation will not be further discussed.

The Application

The Problem

Imagine you own a tow truck that can transport a vehicle, and you are going to travel across the country in it; maybe you are going to pick up some vehicle or are taking a vehicle to another town. Wouldn't it be great if you could make some money both ways? But how do you easily find a towing job? The solution is Carolina, a Mobile RPG application for your phone that will help you locate vehicles in need of transportation. You will be able to search for potential jobs, contact the person soliciting the job, and plan the path to the job's destination.

A solution

So, given your years of experience in IT you sit down to think about it and come up with a mock-up of the Carolina app, shown in Figure 1:

Mock of Carolina App
Fig. 1 The Carolina Application

The application's first screen is at the top left; it will be a menu where the tow truck driver can search for a job either by type of car or by job ID. It also has a menu option to view a management dashboard.

Selecting the Body and Size option will take the user to a page that shows a clickable chart with information on the available jobs by type of car; the user's tow truck may be able to carry only specific sizes of vehicles.

Clicking on a particular body and size column will show a list with all the available jobs in that category, and clicking on any of the shown vehicles will open the Job page, showing all the job-related information. The Job page also has a map with the suggested delivery route, and pictures of the vehicle as it was when picked up and at delivery.

Back to the main menu, selecting Find by Job ID will bring the Search Job page, and inputing the proper Job ID will show the Job page.

Finally, selecting the Management dashboard will bring a page where summarized information about revenues is shown monthly in column chart form, and by state in pie chart form.

Data Files

In terms of data files we start with two: one with the vehicle information, which we'll call VEHICLE, and one with the job information, which we'll call JOB. Both files can be found in the ASNAMRSAMP library. You can recreate the files from DDS if you want. The VEHICLE file contains the descriptive information for a vehicle. It has a unique vehicle identifier, VHID, to link the vehicle to a job, and standard vehicle information like VIN, year, make, model, etc., and is defined as:

A      R RVEHICLE
A     VHID        10     TEXT('ID')
A     VHVIN       17     TEXT('Serial Number')
A     VHMAKE      20     TEXT('Make')
A     VHMODEL     40     TEXT('Model')
A     VHYEAR       4P 0  TEXT('Year')
A     VHPLATE     10     TEXT('Plate')
A     VHCOLOR     20     TEXT('Color')
A     VHRUNS       1     TEXT('Is drivable')
A     VHBODY      10     TEXT('Body Style')
A     VHSIZE      10     TEXT('Size')

The JOB file contains information about a towing job. It has a unique job identifier, the unique vehicle identifier to link to the vehicle's record in the VEHICLE file, and from and to addresses to specify where to pick the vehicle up and where to deliver it. Here's the file's definition:

A      R RJOB
A     JBID        10     TEXT('Job ID')
A     JBVHID      10     TEXT('Vehicle ID')
A     JBFRMFIRST  30     TEXT('From First Name')
A     JBFRMINIT    1     TEXT('From Initial')
A     JBFRMLAST   30     TEXT('From Last Name')
A     JBFRMSTR1   50     TEXT('From Street 1')
A     JBFRMSTR2   50     TEXT('From Street 2')
A     JBFRMCITY   20     TEXT('From City')
A     JBFRMSTATE   2     TEXT('From State')
A     JBFRMZIP5    5     TEXT('From Zip Code')
A     JBFRMCNTRY  30     TEXT('From Country')
A     JBFRMPH1    10P 0  TEXT('Telephone 1')
A     JBFRMPH2    10P 0  TEXT('Telephone 2')
A     JBFRMPCELL  10P 0  TEXT('Cell phone')
A     JBTOSTR1    50     TEXT('To Street 1')
A     JBTOSTR2    50     TEXT('To Street 2')
A     JBTOCITY    20     TEXT('To City')
A     JBTOSTATE    2     TEXT('To State')
A     JBTOZIP5     5     TEXT('To Zip Code')
A     JBTOCNTRY   30     TEXT('To Country')

Now we have enough information to start developing the Carolina application. You can play with a full version of the Carolina application at https://demo.asna.com/Carolina. Works best using a phone!

Lesson 1 - Creation of the Base Mobile RPG App

In preparation for starting on the Carolina application we'll first review some basic concepts of ASNA Mobile RPG, which we will refer to simply as Mobile RPG or MR in this Tutorial, and create a default Mobile RPG website.

1.1 How Mobile RPG works

Mobile RPG takes advantage of the Rational Open Access, RPG Edition (OAR) product on the IBM i. OAR allows the input/output operations on a file to be intercepted by a Handler, which then decides how to process these operations. Mobile RPG adds a Handler that specifically works on workstation files and redirects the workstation's I/O operations to a website ASPX is the .Net version of Active Server Pages (ASP). hosted on a Windows ASPX web server. In this way Mobile RPG presents a web face on traditional RPG ILE programs.

A Mobile RPG program starts when a browser accesses the Mobile RPG website. The web server calls the RPG program on the IBM i server, and the RPG program answers the browser's request by simply doing an EXFMT on a workstation file. The Mobile RPG handler intercepts this EXFMT and communicates with the Mobile RPG components on the web server to deliver the proper web page and data to the browser.

The development cycle of a Mobile RPG application is very similar to the development of a green-screen RPG application. The next table compares traditional RPG vs Mobile RPG development:

Traditional RPG ASNA Mobile RPG
RPG code RPG ILE on the IBM i server RPG ILE on the IBM i server
Display Files Edited as DDS source and compiled on the IBM i Edited as ASPX web pages on Windows using MS Visual Studio, then exported to the IBM i

In both cases the RPG ILE code is developed and compiled on the IBM i. The difference is where the source for the DDS resides. In green-screen programming it resides in the IBM i server. In Mobile RPG, it's coded as an ASPX web page, called a Mobile Display File, or MobileDF.

Developing a Mobile RPG application is an iterative process, done both in the Windows development box, which acts as a Web server, and the IBM i server, as shown in Figure 2:

MR Development Cycle
Fig. 2 Mobile RPG Development Cycle.

1.2 The Default App

Now we are ready, and this is how you will start any new Mobile RPG application you write:

New Web Site
Fig. 3 Visual Studio New Web Site Dialog

In order to properly create the website, the MR website Wizard requires a connection to the IBM i server that will be used to develop the application, so you will now see the dialog in Figure 4 to select the IBM i server and the login credentials to use on it. It is very important that the given credentials have enough authority to create libraries and files, and to compile RPG ILE and DDS.

You can also use a Database Name, which is a cached description of the connection parameters to an IBM i server. To create a Database Name you need to use DataGate Explorer. There's a brief step-through in Appendix A, see your ASNA documentation for further details. There is a slight but significant difference in the behavior of the created website when using credentials vs a Database Name. If you use login credentials, the website user will be asked for user and password to start the application. If you use a Database Name, the application starts using the credentials cached in the Database Name.

New Web Site
Fig. 4 IBM i Credentials Dialog

Type in your login credentials, and select the library that will be used for the IBM i side of the application. We suggest XXCAROLINA, where XX represents your initials so other co-workers following this tutorial don't mistakenly erase or change your work. Click OK and after a few seconds the MR Wizard will have created the base website from which you can start building your application. Figure 5 shows what you should expect to see:

Hello World!
Fig. 5 The Default Mobile RPG App

1.3 A Quick Tour of the Base Website

If the pane on the right hand side is not visible, go to VIEW > Solution Explorer. The Solution Explorer shows the structure and files of the created website.

Two of the source files are of special interest. Web.config is an XML source file that contains settings used by the login process. Let's looks at that part of the file:

<appSettings>
    <add key="MobileRPGUsername" value="xxxx"/>
    <add key="MobileRPGServerName" value="elm"/>
    <add key="MobileRPGServerPort" value="5042"/>
    <add key="MobileRPGPromptForServer" value="no"/>
    <add key="MobileRPGLibraryName" value="XXCAROLINA"/>
    <add key="MobileRPGProgramName" value="HelloRPG"/>
</appSettings>

The user, server, port, and library values are the ones that were provided in the credentials dialog of the wizard. These values can be easily changed to whatever’s needed for deployment. When the app runs, the SignOn page is displayed first asking for the user and the password to connect to the server and start the app. Whether the SignOn page also prompts for the server and port is determined by the value of the “MobileRPGPromptForServer” entry. Any value other than no will cause the prompting of the server and port in addition to user and password. If the login credentials are valid, the app starts by calling the program indicated in the “MobileRPGProgramName” entry, set to HelloRPG which is the name of the program created by the wizard. If any of these key-value pairs is missing, there are defaults hardcoded in the MobileRPGJob.cs source file, put there by the wizard.

If you used a Database Name in the credentials dialog of the wizard you'll see these keys instead:

<appSettings>
    <add key="MobileRPGDatabaseName" value="My400_5042"/>
    <add key="MobileRPGPromptForServer" value="no"/>
    <add key="MobileRPGLibraryName" value="XXCAROLINA"/>
    <add key="MobileRPGProgramName" value="HelloRPG"/>
</appSettings>

Instead of server, port, user and password, the Database name selected in the credentials screen of the wizard is put here. The big difference in app behavior is that when using a Database name, You can use a Database name for development and a user/password for deployment or viceversa, just edit Web.config and change the proper settings. the SignOn page is bypassed and the app starts immediately using the credentials cached in the Database name. This implies that the “MobileRPGPromptForServer” value is ignored. If you need to prompt for a server you must not use a Database name.

Now let's take a look at the Mobile Display File: HelloDspf.aspx. This is the display file used by the startup program "HelloRPG" that we have specified in Web.config, and which was created by the wizard in your IBM i. If it's not open, double-click on it. If it's open but you don't see the text select Split or Source at the bottom of the window. (If you don't see the Design, Split, and Source buttons, right click on HelloDspf.aspx in the solution explorer, click Open With, select Web Forms Editor in the following dialog. Click Set as Default then OK.) ASPX source files are special text files containing markup, a combination of ASPX tags and HTML tags, that when processed by the web server produce an HTML page to be sent to a browser.

Take your time reading the comment, in green, at the top of the ASPX source. DDS controls define both data and presentation. You should read the Mobile Display File Overview document, which explains in detail the structure of the Mobile Display File and the different DDS controls. To specify records and fields we use a set of ASPX controls designed to be used by Mobile RPG, called the DDS controls. You can see these controls in the Toolbox (go to VIEW > Toolbox if you can't see the Toolbox window, use the thumbtack to pin it so it won't autohide). See Figure 6.

List of DDS controls
Fig. 6 The VS Toolbox

Going back to HelloDspf.aspx, look for the second asp:Content element. Inside it is the definition of the DDS file. It contains one DDS record, and the record itself has a DDS bar with two DDS bar segments, and a HTML element that shows the 'Hello World!' text. This is the basic structure of a Mobile display file: a DDS file, with one or more DDS records containing various DDS controls and HTML.

In the IBM i the wizard created the library XXCAROLINA (if it didn't already exist) and three files: QRPGLESRC with the HELLORPG member, the HELLODSPF display file, and the compiled HELLORPG program, shown in Figure 7:

XXCAROLINA library
Fig. 7 The XXCarolina Library.

Now run the website by pressing F5 on your keyboard. After VS builds your website your default browser will open with the first page of your app, which is the signon screen, in Figure 8:

Signon Screen
Fig. 8 The Signon Screen

enter your IBM i login credentials to start the app which shows the Hello World screen, shown in Figure 9: If you have a mobile device connected via Wi-Fi to the local network you may setup your Carolina Mobile App under Internet Information Services (IIS), and test it. See Appendix B for a brief overview of the required steps.

Hello World!
Fig. 9 The Hello World! Screen

Pressing END will finish the app which will show the End of Job screen, in Figure 10:

Thanks and Bye
Fig. 10 End of Job

Finally, there is some rebranding we need to do to better reflect the name of the application we are developing (and to prevent the inadvertent loss of our work in case we start over!).

  1. Let's rename HelloDspf.aspx to CaroMain.aspx. You do this by right-clicking on the file in Solution Explorer and selecting Rename. Type in the new name.
  2. Let's also rename the class that's part of the ASPX website processing. Open CaroMain.aspx.cs by double-clicking on it. Locate the line that contains class HelloDspf. Right-click on HelloDspf, and select Refactor > Rename, as seen in Figure 11:
    Rename class
    Fig. 11 Refactoring the Class Name

    In the Rename dialog type in the new name CaroMain.
  3. Open CaroMain.aspx, find the text Mr Hello and replace it with Carolina.
  4. In Solution Explorer, go to the Monarch folder, open SignOn.aspx and look for the Sign on to Hello World text. Replace it with Sign on to Carolina. See Figure 12. Now go to the Themes folder, open MasterPage.master and look for Mr Hello. Replace it with Carolina.
    Rename class
    Fig. 12 Finding Files in Solution Explorer

  5. Now we need to tell the RPG program that we renamed the display file. Using your favorite IBM i editing tool, first rename the RPG source member from HELLORPG to CARORPG. Now edit the CARORPG source member and change the name of the workstation file to CAROMAIN, the name of your Mobile DF.
  6. Go back to the Solution Explorer in VS and open Web.config. Change the name of the startup program to CARORPG.
    <add key="MobileRPGProgramName" value="CARORPG">
  7. To create the DSPF object needed by the RPG compiler we need to Export the Mobile Display File to the IBM i. Right-click on CaroMain.aspx, and select Export to Display File... Enter the login parameters and Library (XXCAROLINA), and click OK.
  8. Verify that the DSPF object CAROMAIN was created in XXCAROLINA. Compile the RPG ILE source.
  9. Run the app from VS by pressing F5.

Lesson 2 - Creating the Home Menu

With this Lesson we start modifying the Mobile Display File by creating the Home Menu screen, seen in Figure 13.

Home Menu Page
Fig. 13 The Home Menu

In this lesson we will:

  1. Add three buttons to the Home Menu, that the user will press/touch to run the application.
  2. Link the buttons to different F keys and indicators to signal the program.
  3. Prepare the RPG code to handle the different indicators.
  4. Update the DSPF object and recompile the RPG code.
  5. Test the modified program.

2.1 Editing the Mobile Display File

Before we start modifying CaroMain.aspx let's review some rules governing the syntax of the markup:

  1. Section delimiters, or Tags, are recognized by the the use of the symbols < and >, and they come in open/close pairs, with the closing Tag being prefixed with the / character. For example, <div> ... </div>. Tags that do not have any nested content can be implicitly closed by adding the / before the >. For example, <br />.
  2. The syntax is free-form. That is, extra whitespace characters, like tabs, spaces, and newlines, do not matter. Use them liberally to improve readability.
  3. Certain characters, like <, are reserved and need to be replaced with a specific escape sequence (see the online list here).
  4. Since newlines are ignored, use a line break tag <br /> when needing paragraph separation.
  5. Tags may contain keywords or properties, which are specified as keyword = "value", for example, <div style="mydivstyle">.
  6. ASPX tag names are prefixed, that is, their names look like prefix:name. ASPX tags require the property runat="server". Mobile RPG tags are ASPX tags using the prefix mdf:.

Open CaroMain.aspx. Click Split at the bottom of the window so you see both the markup and the design. Find the Here is a quick reference to HTML tags. Some that you should become familiar with are h1 through h6, p, br, hr, div, and span Hello, world! text, and remove it. Before:

    </mdf:DdsBar>

    Hello, world!
    <br/>
</mdf:DdsRecord>

after:

    </mdf:DdsBar>

    <br/>
</mdf:DdsRecord>

In its place add a header, level 2, saying Home Menu, and a header, level 4, saying Locate Transportation Jobs.

    </mdf:DdsBar>
    <h2>Home Menu</h2>
    <h4>Locate Transportation Jobs</h4>
</mdf:DdsRecord>

Be sure to click the yellow bar to keep your views in sync, as shown in Figure 14:

Click to sync
Fig. 14 Synchronize Views

Now we'll use a div tag to create a block where we'll put a button and its text. Write the open tag of the div, and VS will write the close for you. You can alternatively find a div in the toolbox, in the HTML section, and drag-and-drop it in the proper place in the text:

    <h2>Home Menu</h2>
    <h4>Locate Transportation Jobs</h4>
    <div></div>
</mdf:DdsRecord>

Go to the toolbox and locate the DdsButton in the ASNA Controls. Drag it and drop it inside the div. You can do this in the Source pane or in the Design pane of the code window, whatever you prefer. Whether you work with the Design view or the Source view is up to you. There are certain operations that are difficult to do in Design, so it's good to get used to working in Source.After the button, type a br line break (or press Enter if you are in the Design pane), and the text By Body & Size:

<div>
    <mdf:DdsButton ID="DdsButton2" runat="server" />
    <br />
    By Body & Size
</div>

Now we need to customize the button for our purposes. Position your cursor on any part inside the DdsButton tag, or click to select the button in Design, and press F4. This should bring up the Properties window. In the Appearance group look for the ButtonStyle property and set its value to Image. Now look for the Image property and set it to ~/Themes/Current/Images/Chart.png, and the ImageAlign property to Middle.

Button Properties

You will see that the text in the editor is changing as you select values for the button properties:

    <div>
        <mdf:DdsButton ID="DdsButton2" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Chart.png" ImageAlign="Middle" />
        <br />
        By Body & Size
    </div>

The Image property is a path to an image file. The ~ in the path means the root of the website. To borrow the images from the Carolina sample, right-click on the Images folder, and select Add > Existing Item. Browse for the folder where the Carolina sample is installed (filter for image files at the bottom right of the browse dialog), and select Chart.png, Dashboard.png, Delete.png, HomeScreenIcon.png, Refresh.png, and Suv.png. See Figure 16:Copy ImagesFig. 16 Adding Files So the button will start from the root of the website, go into Themes, then Current, then Images, and look in that folder for the Chart.png file to be its image. For now, this image file doesn't exist, but we can borrow it and a few others from the Carolina sample, or you can make your own.

Now, we need to link the button with a function key. For this, the button has a property called AidKey. Go to the Behavior group in the property window and set the AidKey property to F14. You can use any F key and any indicator you want. Finally, we need to enable the F14 key in the record and assign it an indicator. Find the DdsRecord declaration and look for the FuncKeys property in the AidKeys group of the property window. The difference between FuncKeys and AttnKeys is that FuncKeys return values to the program, while AttnKeys don't. It's a common mistake to put in AttnKeys what should go in FuncKeys. If your RPG program doesn't seem to receive data check here first! Type in F14 14;; this selects indicator 14 to be associated with key F14. See Figure 17:

Func Keys
Fig. 17 Wiring Indicators in the Record Format

You'll notice that F3 is set to turn on indicator 03, and this is to support the Exit button in the top navigation bar.

Now go and add a second div for the By Job ID button and text. Use the ~/Themes/Current/Images/Suv.png image, and F13 with indicator 13 as the Aid key and indicator. You may want to add a couple of line breaks in between the divs, use your good taste. And of course you are allowed to use Copy/Paste in the ASPX editor. Do not forget to set the FuncKeys in the DdsRecord, otherwise your button will not be visible when you run the app. You should end up with something like this:

<h2>Home Menu</h2>
<h4>Locate Transportation Jobs</h4>
<div>
    <mdf:DdsButton ID="DdsButton2" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Chart.png" ImageAlign="Middle" AidKey="F14" />
    <br />
    By Body & Size
</div>
<br />
<div>
    <mdf:DdsButton ID="DdsButton3" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Suv.png" ImageAlign="Middle" AidKey="F13" />
    <br />
    By Job ID
</div>

To finish the Home Menu we need to add another h4 header containing the text Dashboard, and another div with a button followed by the text Management. The button will use F15 with indicator 15, and the image ~/Themes/Current/Images/Dashboard.png. Put all this right after the Job ID div section:

<div>
    <mdf:DdsButton ID="DdsButton3" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Suv.png" ImageAlign="Middle" AidKey="F13" />
    <br />
    By Job ID
</div>
<br />
<h4>Dashboard</h4>
<div>
    <mdf:DdsButton ID="DdsButton4" runat="server" ButtonStyle="Image" Image="~/Themes/Current/Images/Dashboard.png" ImageAlign="Middle" AidKey="F15" />
    <br />
    Management
</div>

Build and run the app by pressing F5. Your screen should look like Figure 18:

Func Keys
Fig. 18 The Home Menu

You may be wondering why we didn't export the Mobile Display File to the IBM i. The answer is we didn't have to because the record format didn't change. DdsButtons don't create fields in the record format or affect it in any way.

Last, change the RPG program to call (empty) subroutines when the different indicators for the buttons are detected. Write this code any way you want, but use BYBDYSZE, BYJOBID, and MGMNTDSH as the names of the subroutines. Here's a possible implementation. You are welcome to use free format if you prefer:

0007.00 C                   Select
0008.00 C                   When      *In13
0009.00 C                   ExSr      BYJOBID
0010.00 C                   When      *In14
0011.00 C                   ExSr      BYBDYSZE
0012.00 C                   When      *In15
0013.00 C                   ExSr      MGMNTDSH
0014.00 C                   When      *In03
0015.00 C                   Eval      *InLR = *On
0016.00 C                   Return
0017.00 C                   EndSl
0018.00
0019.00 C******************************************
0020.00 C* Search for Jobs
0021.00 C******************************************
0022.00 C     BYJOBID       BegSr
0023.00 C                   EndSr
0024.00
0025.00 C******************************************
0026.00 C* Search by body and size of vehicle
0027.00 C******************************************
0028.00 C     BYBDYSZE      BegSr
0029.00 C                   EndSr
0030.00
0031.00 C******************************************
0032.00 C* Management Dashboard
0033.00 C******************************************
0034.00 C     MGMNTDSH      BegSr
0035.00 C                   EndSr
0036.00

Compile your RPG program. Run the website again from VS to verify all is well.

Extra Credit

Add a DdsCharField after the Dashboard div. Change its ID to Operation, and set its Usage to OutputOnly. Export the Display File to the IBM i (you need this because the DdsCharField represents a new field in the record format. Extra-extra credit: what is its length? hint: look for Length in the Property Window). In each of the RPG subroutines set the Operation field to "By Job", "Body&Size", and "Management" respectively. Compile the RPG program, run the app, and click on the different buttons.

Solution

Here's the DdsCharField:

<mdf:DdsCharField ID="Operation" runat="server" CssClass="DdsCharField" Usage="OutputOnly" />

And here's just one of the subroutines. The other two are similar.

0022.00 C     BYJOBID       BegSr
0023.00 C                   Eval      Operation = 'By Job ID'
0024.00 C                   EndSr

Extra-extra credit: the default length of a DdsCharField is 10.

Lesson 3 - More Record Formats

In this lesson we are going to add two new screens. The first one is the Job Search screen, and the second one is the Job screen, seen in Figure 19:

Search for a Job The Job
Fig. 19 The Job Search Screens

In this lesson we will:

  1. Add a record format for each one of the screens we are designing.
  2. Create navigation bars for both screens.
  3. Create a footer bar for the Job screen.
  4. Add the necessary Dds controls to the Job Search screen.
  5. Add grouping placeholders for the information that will be displayed in the Job screen. This information will be filled in in the next lesson.

3.1 The Search By ID Record Format

Open The CaroMain.aspx. From the Toolbox, drag and drop a DdsRecord and put it right after the existing DdsRecord (i.e., right after the </mdf:DdsRecord>). You'll get this:

    </mdf:DdsRecord>
    <mdf:DdsRecord ID="DdsRecord1" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;">
    </mdf:DdsRecord>

</asp:Content>

Let's take a look at the DdsRecord default properties. ID is the name of the ASPX control, it's created by the editor when you drag and drop the control on a page. This name must be unique. The runat property specifies that the control is a server control, which means it has code that runs in the web server to produce the HTML for the browser. You can see all the predefined Css styles in the drop-down for the CssClass value in the Property Window. These styles live in the .css source files inside the ~/Themes/Current/Styles folder of the website. Erase Formats is a list of the record formats to be erased from the file when writing to this record, and CssClass determines the visual styling of the control when shown on the browser. The Style value indicates that the position of the record on the page is relative, i.e. is not fixed to some arbitrary place when viewed in a browser. This allows the browser to decide the best accommodation for the record. You may have already noticed that what the browser shows at runtime is not exactly identical to what you see in Design.

Now go to the Properties window and set the Alias property to JobSrch. You can leave the ID property as it is, but it's better if you give it a more meaningful name. It's considered good practice to name it just slightly different than the Alias, by prefixing the Alias value with _ if it's a record, or by record-name_ if it's a field. Strictly speaking, ID represents the name of the control in the ASPX page, while Alias represents the name of the entity (field, record, or file) in the RPG program. If Alias is not given, then ID will also represent the name of the entity in the RPG program. This distinction is needed because ASPX requires unique names for its controls, while RPG record formats can share field names.

Alias and ID properties

What does the record contain? Let's take a look again at the page we are creating:

Search for a Job

The record has a bar at the top with a Home button and some text, and the body of the record has a grouping with text, an input field, and a Search button. We'll start with the bar. Drag and drop a DdsBar control into the DdsRecord (again, you can do this in the Source or Design panes) and change its ID to JobSrchBar. You'll get something like this:

<mdf:DdsRecord ID="_JobSrch" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="JobSrch">
    <mdf:DdsBar ID="JobSrchBar" runat="server" CssClass="DdsBar">
        <mdf:DdsBarSegment runat="server" Alignment="Center">
        </mdf:DdsBarSegment>
    </mdf:DdsBar>
</mdf:DdsRecord>

To set the bar as the navigation bar for the record set the record's NavigationBarControlID property to JobSrchBar. You can see the bar already contains a DdsBarSegment. Change the bar segment's Alignment to Left. Add a button in the bar segment (drag and drop a DdsButton into it). The button style NavButton matches the style of the navigation bar. Set the button's CssClass to NavButton, AidKey to F12, and Text to Home:

<mdf:DdsBarSegment runat="server" Alignment="Left">
    <mdf:DdsButton ID="DdsButton5" runat="server" CssClass="NavButton" AidKey="F12" Text="Home" />
</mdf:DdsBarSegment>

Now let's enable the button's AidKey as an Attention Key in the record and pair it with indicator 12. Indicator 12 will then tell the RPG program to return to the Home Menu screen. This is what the record looks like now:

<mdf:DdsRecord ID="_JobSrch" runat="server" CssClass="DdsRecord"
 EraseFormats="*ALL" Style="position: relative;"
 Alias="JobSrch" NavigationBarControlID="JobSrchBar" AttnKeys="F12 12;">

Add a second DdsBarSegment. Set its Alignment to Center. Inside this bar segment type Search By ID.

<mdf:DdsBarSegment ID="DdsBarSegment3" runat="server" Alignment="Center">
    Search By ID
</mdf:DdsBarSegment>

Let's use a <span> around the text to set some formatting on it:The class property on an HTML tag is equivalent to the CssClass property on a server control tag.

<span class="PanelTitle">Search By ID</span>

For the body of the record we need a grouping control that will contain the rest of the elements of the screen and give them a nice look. This control is the DdsPanel. Look for DdsPanel in the toolbar and add it to the record after the DdsBar. The grouping text of a DdsPanel can also be given at runtime, by setting the name of the field that contains the text in the GroupingTextField and GroupingTextFieldLength properties. If you do this, the field becomes part of the record format. Set its GroupingText property to Please enter the:

    </mdf:DdsBar>
    <mdf:DdsPanel ID="DdsPanel1" runat="server" CssClass="DdsPanel" GroupingText="Please enter the">
    </mdf:DdsPanel>
</mdf:DdsRecord>

Now add a DdsConstant and a DdsCharField for the label and the input field on the record. Set the Text property of the constant to Job ID. Set the Alias of the char field to JBID (and its ID to JobSrch_JBID, its Length to 10, and its Usage to InputOnly. This is the name of the key field in the JOB file, so declaring it also here will simplify the RPG code for reading a JOB record. You can also set the ID of these controls to more meaningful names if you want:

<mdf:DdsPanel ID="DdsPanel1" runat="server" CssClass="DdsPanel" GroupingText="Please enter the">
    <mdf:DdsConstant ID="DdsConstant1" runat="server" CssClass="DdsConstant" Text="Job ID" />
    <mdf:DdsCharField ID="JBID" runat="server" CssClass="DdsCharField" Alias="JBID" Usage="InputOnly" />
</mdf:DdsPanel>

Finally, add the Search DdsButton after the char field. Set its ButtonStyle property to Button, Text property to Search, and AidKey property to Enter. This sets the button click to behave as if the user pressed Enter on the keyboard:

<mdf:DdsButton ID="DdsButton6" runat="server" Text="Search" AidKey="Enter" ButtonStyle="Button" />

The changes to the MobileDF are finished. Make sure that the CaroMain.aspx page builds by right-clicking on the file in Solution Explorer and selecting Build Page. See Figure 21:

Build Page
Fig. 21 Building a Page

If the Build is successful, run the Export process to create the display file on the IBM i.

Now we can modify the RPG code to process the By Job ID button of the Home Menu. Edit the BYJOBID subroutine to execute the new record format:

0022.00 C     BYJOBID       BegSr
0023.00 C                   ExFmt     JobSrch
0024.00 C                   If        *In12
0025.00 C                   LeaveSr
0026.00 C                   EndIf
0027.00 C                   EndSr

Save and compile. Go back to VS and run the app. The By Job ID button should open the new screen, as shown in Figure 22:

Search by Job ID
Fig. 22 The Job Search Screen

Although we are not yet handling the Search button, the Home button should work and bring you back to the Home Menu.

You may notice that the text in the navigation bar is aligned to the right. We can move it to the middle by adding an empty bar segment after the text, as bar segments split the navigation bar area among them. Go ahead and add it (and add a couple of line breaks before the panel too):

    ...
        <span class="PanelTitle" >Search By ID</span>
    </mdf:DdsBarSegment>
    <mdf:DdsBarSegment ID="DdsBarSegment4" runat="server"></mdf:DdsBarSegment>
</mdf:DdsBar>
<br /><br />
<mdf:DdsPanel ID="DdsPanel1" runat="server" CssClass="DdsPanel" GroupingText="Please enter the">
    ...

Run the program again, no need to export and recompile, and see that the label is now in the center of the bar, shown in Figure 23:

Search by Job ID
Fig. 23 Fixing the Job Search Screen

3.2 The Job Details Screen

We are now ready to add the record format for the screen that shows the Job details, shown in Figure 24:

The Job
Fig. 24 The Job Screen

Add a new DdsRecord to the file. Set its Alias to JobRec, set its ID using the _ naming convention. Add two DdsBars, one will be the top navigation bar, and one will be the footer bar. Name the first one JobNavBar, and the second one JobFooterBar. Attach the bars to the record by setting the record's NavigationBarControlID and FooterBarControlID properties to the IDs of the bars:

<mdf:DdsRecord ID="_JobRec" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="JobRec" NavigationBarControlID="JobNavBar" FooterBarControlID="JobFooterBar">
    <mdf:DdsBar ID="JobNavBar" runat="server" CssClass="DdsBar">
        <mdf:DdsBarSegment ID="DdsBarSegment5" runat="server" Alignment="Center"></mdf:DdsBarSegment>
    </mdf:DdsBar>
    <mdf:DdsBar ID="JobFooterBar" runat="server" CssClass="DdsBar">
        <mdf:DdsBarSegment ID="DdsBarSegment6" runat="server" Alignment="Center"></mdf:DdsBarSegment>
    </mdf:DdsBar>

</mdf:DdsRecord>

The navigation bar has 3 segments, the first one with the Back button, the middle one shows the job number, and the last one has two buttons that allow the user to move to the previous or next records in the JOB file. Add the bar segments and their contents to the navigation bar. Start by adding the 2 additional DdsBarSegment controls, set their alignments to Left, Center, and Right respectively. Add the Back button to the first segment, with AidKey set to F12, and CssClass set to NavButton. Add the text Job: XXXX to the second (middle) one. In the last bar segment add two buttons with the characters < and > as text and AidKeys set to F7 and F8 respectively; set the buttons CssClass to NavButtonSymbol:

<mdf:DdsBarSegment ID="DdsBarSegment5" runat="server" Alignment="Left">
    <mdf:DdsButton ID="DdsButton7" runat="server" ButtonStyle="Button" Text="Back" CssClass="NavButton" AidKey="F12" />
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment7" runat="server" Alignment="Center">
    <span class="PanelTitle">Job: XXXX</span>
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment8" runat="server" Alignment="Right">
    <mdf:DdsButton ID="DdsButton8" runat="server" ButtonStyle="Button" AidKey="F7" Text="<" CssClass="NavButtonSymbol" />
    <mdf:DdsButton ID="DdsButton9" runat="server" ButtonStyle="Button" AidKey="F8" Text=">" CssClass="NavButtonSymbol" />
</mdf:DdsBarSegment>

Don't forget to set the record's AttnKeys with the F keys for the buttons, pairing them with the same-numbered indicators.

AttnKeys="F7 07;F8 08;F12 12;"

The footer bar has two segments, aligned Left and Right respectively, and each segment contains a single button. The first segment contains the Refresh button, and the second segment the Delete button. Both buttons show images instead of text and are of NavButton style. The Refresh button shows the ~/Themes/Current/Images/Refresh.png image and uses F5, while the Delete button shows the ~/Themes/Current/Images/Delete.png image and uses F4. Add these segments and buttons now, and add the F keys to the record's AttnKeys:

<mdf:DdsBarSegment ID="DdsBarSegment6" runat="server" Alignment="Left">
    <mdf:DdsButton ID="DdsButton10" runat="server" ButtonStyle="Image" AidKey="F5" Image="~/Themes/Current/Images/Refresh.png" />
</mdf:DdsBarSegment>
<mdf:DdsBarSegment ID="DdsBarSegment9" runat="server" Alignment="Right">
    <mdf:DdsButton ID="DdsButton11" runat="server" ButtonStyle="Image" AidKey="F4" Image="~/Themes/Current/Images/Delete.png" />
</mdf:DdsBarSegment>

As for the information displayed on the screen, for now, as we said at the start of this lesson, we will only show place holders for each of the five groups of information. Add five DdsPanels with some descriptive text in each, with a line break before each one:

<br />
<mdf:DdsPanel ID="DdsPanel2" runat="server" CssClass="DdsPanel">
    Link to external site</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel3" runat="server" CssClass="DdsPanel">
    FROM address</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel4" runat="server" CssClass="DdsPanel">
    Map</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel5" runat="server" CssClass="DdsPanel">
    Pictures at source</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel6" runat="server" CssClass="DdsPanel">
    Pictures at destination</mdf:DdsPanel>

Build and Export CaroMain.aspx. We can change now the RPG code to process the Search button of the Search By ID screen. Create the subroutine SHOWJOB to show the new record format we added. Let's also handle the Back button of the Job screen and set placeholders for the Refresh and Delete buttons. We can also setup the program structure to handle the different indicators set by the buttons:

0022.00 C     BYJOBID       BegSr
0023.00 C     SrchJob       Tag
0024.00 C                   ExFmt     JobSrch
0025.00 C                   If        *In12
0026.00 C                   LeaveSr
0027.00 C                   EndIf
0028.00 C                   ExSr      SHOWJOB
0029.00 C                   If        *In12
0030.00 C                   GoTo      SrchJob
0031.00 C                   EndIf
0032.00 C                   EndSr
0033.00
0034.00 C******************************************
0035.00 C*   Show Job record
0036.00 C******************************************
0037.00 C     SHOWJOB       BegSr
0038.00 C                   ExFmt     JobRec
0039.00 C                   Select
0040.00 C                   When      *In12
0041.00 C                   LeaveSr
0042.00 C                   When      *In05
0043.00 C*      refresh
0044.00 C                   When      *In04
0045.00 C*      delete record
0046.00 C                   EndSl
0047.00 C                   EndSr

Finally, let's read the job and vehicle information we need for the Job screen and use the job number in the navigation bar. To find the Job and vehicle information we'll use a logical file that combines records from both the JOB and the VEHICLE files using the vehicle ID which exists in and links both files. The logical will have the job ID field, JBID, as the key. Here's the DDS description of the JOBVEH01 logical file:

A     R RJOBVEH		 JFILE(JOB VEHICLE)
A     J           		 JOIN(JOB VEHICLE)
A                 		 JFLD(JBVHID VHID)
A       VHID        10      TEXT('ID')
A       VHVIN       17      TEXT('Serial Number')
A       VHMAKE      20      TEXT('Make')
A       VHMODEL     40      TEXT('Model')
A       VHYEAR       4P 0   TEXT('Year')
A       VHPLATE     10      TEXT('Plate')
A       VHCOLOR     20      TEXT('Color')
A       VHRUNS      1       TEXT('Is drivable')
A       VHBODY      10      TEXT('Body Style')
A       VHSIZE      10      TEXT('Size')
A       JBID        10      TEXT('Job ID')
A       JBVHID      10      TEXT('Vehicle ID')
A       JBFRMFIRST  30      TEXT('From First Name')
A       JBFRMINIT    1      TEXT('From Initial')
A       JBFRMLAST   30      TEXT('From Last Name')
A       JBFRMSTR1   50      TEXT('From Street 1')
A       JBFRMSTR2   50      TEXT('From Street 2')
A       JBFRMCITY   20      TEXT('From City')
A       JBFRMSTATE   2      TEXT('From State')
A       JBFRMZIP5    5      TEXT('From Zip Code')
A       JBFRMCNTRY  30      TEXT('From Country')
A       JBFRMPH1    10P 0   TEXT('Telephone 1')
A       JBFRMPH2    10P 0   TEXT('Telephone 2')
A       JBFRMPCELL  10P 0   TEXT('Cell phone')
A       JBTOSTR1    50      TEXT('To Street 1')
A       JBTOSTR2    50      TEXT('To Street 2')
A       JBTOCITY    20      TEXT('To City')
A       JBTOSTATE    2      TEXT('To State')
A       JBTOZIP5     5      TEXT('To Zip Code')
A       JBTOCNTRY   30      TEXT('To Country')
A     K JBID

This file already exists in the ASNAMRSAMP library, or if you want you can recreate it in your own XXCAROLINA library out of the DDS code.

Let's modify the navigation bar of the Job screen, in the JobRec record format, to show the Job ID. We know that the Job ID is in the JBID field of the JOBVEH01 file so we'll make this field part of the MobileDF record format. Remove the XXXX from the text in the navigation bar, and add after the </span> a DdsCharField to declare the OutputOnly field JBID with the same type and length as in the JOBVEH01 file. Remember that field names go in the Alias property, and set the ID to the same name, prefixed with JobRec_. Use PanelTitle for its CssClass to match the preceding text:

<span class="PanelTitle">Job: </span>
<mdf:DdsCharField ID="JobRec_JBID" runat="server" CssClass="PanelTitle" Alias="JBID" Length="10" Usage="OutputOnly" />

Build and Export the page.

Add the code to the RPG program to read the record with the JBID key provided by the user in the Job Search screen. Add code to handle *In07 and *In08 (previous record and next record):

0002.00 FJOBVEH01  IF   E           K DISK
...
0050.00 C     BYJOBID       BegSr
0051.00 C     SrchJob       Tag
0052.00 C                   ExFmt     JOBSRCH
0053.00 C                   If        *In12
0054.00 C                   LeaveSr
0055.00 C                   EndIf
0056.00 C     JBID          Chain     JOBVEH01                           3030
0057.00 C                   If        *In30
0058.00 C                   GoTo      SrchJob
0059.00 C                   EndIf
0060.00 C     ShwNxtPrv     Tag
0061.00 C                   ExSr      SHOWJOB
0062.00 C                   Select
0063.00 C                   When      *In12
0064.00 C                   GoTo      SrchJob
0065.00 C                   When      *In07
0066.00 C                   ReadP     JOBVEH01                             3030
0067.00 C                   When      *In08
0068.00 C                   Read      JOBVEH01                             3030
0069.00 C                   EndSl
0070.00 C                   If        *In30
0071.00 C     JBID          Chain     JOBVEH01                           3030
0072.00 C                   GoTo      ShwNxtPrv
0073.00 C                   EndIf
0074.00 C                   If        *In07 or *In08
0075.00 C                   GoTo      ShwNxtPrv
0076.00 C                   EndIf
0077.00 C                   EndSr

Compile your RPG program, and run the app from VS. To test, the valid Job ID keys go from J01 to J20. You should see something like Figure 25:

Search for a Job The Job
Fig. 25 The Job Search and Job Screens

Lesson 4 - Completing the Job Screen

This lesson continues the work we started in lesson 3. In this lesson we will:

  1. Set the vehicle info with a link to an external site.
  2. Set the From and To addresses, with clickable phone numbers that will prompt the phone to dial.
  3. Set the map control showing the route to the destination.
  4. Set the display of pictures at source, with functionality to allow adding more pictures.
  5. Set the display of pictures at destination, with functionality to allow adding more pictures.

4.1 Creating a Link

We start with the first panel, which will display a hyperlink to an external website showing generic information for the particular brand, model, and year of vehicle. For this we'll use the DdsLink control. Many of the Mobile RPG controls support two ways to handle text values via properties. You can either provide a constant at design time, when you are creating the MobileDF, or at runtime, via fields set in the RPG program. The properties that support this behavior follow this naming convention: XyzValue, which is used to set a design-time constant, and XyzFieldName paired with XyzFieldLength, which declare a field in the record format that will contain the value at runtime. In the DdsLink both the Text to display and the Url to link to are of this kind. This control makes it easy to set a hyperlink on the page, so that the hyperlink will be invoked when the user clicks or taps on the text. We start by deleting the placeholder text and adding a DdsLink control in its place. We'll use fields to control the text to be shown and the url to jump to. Set the name of the field for the text to VEHDESC with a length of 50, and set the url field to SITESPECS with a length of 128:

<mdf:DdsLink ID="DdsLink1" runat="server" TextFieldLength="50" UrlFieldName="SITESPECS" UrlFieldLength="128" TextFieldName="VEHDESC" />

The information to create the Text and the Url comes from the VHMAKE, VHMODEL, and VHYEAR fields in the logical file JOBVEH01. For the Text, we'll show Year Make Model, e.g. 2006 Jeep Wrangler Unlimited. For the Url, we'll be using www.edmunds.com (a very popular new and used auto information website). They have a comprehensive catalog for people looking into buying or selling vehicles.

When you use the www.edmunds.com site you can look for a particular vehicle specs and inspect the URL. They have their pages organized so that the spec HTML file is saved under a make/model/year folder structure. For example, to get to the 2003 Toyota Tundra, you can just type the following URL on your website:

http://www.edmunds.com/toyota/tundra/2003/features-specs.html

so we'll construct that string out of the vehicle data. We just need to ensure that it's in lower case. Edit the RPG code to set both the VEHDESC and SITESPECS fields:

0004.00 Dlower            C                   Const('abcdefghijklmnopqrstuvwxyz')
0005.00 DUPPER            C                   Const('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
...
0045.00 C     SHOWJOB       BegSr
0046.00 C                   Eval      VEHDESC = %char(VHYEAR) + ' ' +
0047.00 C                             %trimr(VHMAKE) + ' ' +
0048.00 C                             %trimr(VHMODEL)
0049.00 C                   Eval      SITESPECS = 'http://www.edmunds.com/' +
0050.00 C                             %trimr(VHMAKE) + '/' +
0051.00 C                             %trimr(VHMODEL) + '/' +
0052.00 C                             %char(VHYEAR) + '/features-specs.html'
0053.00 C     UPPER:lower   XLATE     SITESPECS     SITESPECS
0054.00 C                   ExFmt     JobRec

Build and export the page, and compile your program. Run the app, search for job J09 (you can search for any job from J01 to J20). You should see something like Figure 26:

External Link
Fig. 26 Link to an External Website

4.2 Name and Addresses

The second panel contains the From address, To address, Name, and Telephone numbers. Go to the panel and remove the placeholder text. Add two DdsCharFields for the From address. Set their Alias properties to FromStr and FromCSZ, Length 60 for both, and OutputOnly for Usage. Add a line break between the fields, and a line break and a horizontal rule (<hr />) after both fields:

<mdf:DdsCharField ID="JobRec_FromStr" runat="server" CssClass="DdsCharField" Alias="FromStr" Usage="OutputOnly" Length="60" />
<br />
<mdf:DdsCharField ID="JobRec_FromCSZ" runat="server" CssClass="DdsCharField" Alias="FromCSZ" Usage="OutputOnly" Length="60" />
<br />
<hr />

Repeat for the To address components, same property values, use ToStr and ToCSZ for the Alias names:

<mdf:DdsCharField ID="JobRec_ToStr" runat="server" CssClass="DdsCharField" Alias="ToStr" Usage="OutputOnly" Length="60" />
<br />
<mdf:DdsCharField ID="JobRec_ToCSZ" runat="server" CssClass="DdsCharField" Alias="ToCSZ" Usage="OutputOnly" Length="60" />
<br />
<hr />

Add one more char field for the full name of the contact, set its Alias to FullName, and declare it OutputOnly and Length 60. Add a line break:

<mdf:DdsCharField ID="JobRec_FullName" runat="server" CssClass="DdsCharField" Alias="FullName" Usage="OutputOnly" Length="60" />
<br />

Looking at the data file we see that there may be up to three phone numbers for the contact. Let's add a DdsConstant and a DdsDecField for the first phone number. Set the Text property of the constant to Phone 1: and give the DdsDecField the same Alias name as the file field, JBFRMPH1, with same Length and Decimals, 10 and 0. Let's also use an editword of 0( )& -, appropriate for displaying phone numbers. Now, what happens if there is no telephone number? In traditional IBM i DDS we can condition the visibility of constants and fields using indicator expressions. It's the same with MobileDFs. For both controls, look for the property named VisibleCondition and set it to 51. This means that the visibility of these controls will be dependent on the value of indicator 51. Add a line break. Repeat for JBFRMPH2 and JBFRMPCELL, conditioning their visibility with indicators 52 and 53, respectively:

<mdf:DdsConstant ID="DdsConstant2" runat="server" CssClass="DdsConstant" Text="Phone 1:" VisibleCondition="51" />
<mdf:DdsDecField ID="JobRec_JBFRMPH1" runat="server" CssClass="DdsDecField" Alias="JBFRMPH1" EditWord="0(   )&   -    " VisibleCondition="51" Length="10" Decimals="0" />
<br />
<mdf:DdsConstant ID="DdsConstant3" runat="server" CssClass="DdsConstant" Text="Phone 2:" VisibleCondition="52" />
<mdf:DdsDecField ID="JobRec_JBFRMPH2" runat="server" CssClass="DdsDecField" Alias="JBFRMPH2" EditWord="0(   )&   -    " VisibleCondition="52" Length="10" Decimals="0" />
<br />
<mdf:DdsConstant ID="DdsConstant4" runat="server" CssClass="DdsConstant" Text="Mobile:" VisibleCondition="53" />
<mdf:DdsDecField ID="JobRec_JBFRMCELL" runat="server" CssClass="DdsDecField" Alias="JBFRMPCELL" EditWord="0(   )&   -    " VisibleCondition="53" Length="10" Decimals="0" />
<br />

Build and Export the page. Edit the RPG source and add the operations to compute FromStr, FromCSZ, ToStr, ToCSZ, FullName, and the value of the visibility indicators *In51, *In52, and *In53:

...
0053.00 C     UPPER:lower   XLATE     SITESPECS     SITESPECS
0054.00 C                   Eval      FromStr = %trimr(JBFRMSTR1) + ' ' +
0055.00 C                             %trimr(JBFRMSTR2)
0056.00 C                   Eval      FromCSZ = %trimr(JBFRMCITY) + ', ' +
0057.00 C                             %trimr(JBFRMSTATE) + ' ' +
0058.00 C                             %trimr(JBFRMZIP5)
0059.00 C                   Eval      ToStr = %trimr(JBTOSTR1) + ' ' +
0060.00 C                             %trimr(JBTOSTR2)
0061.00 C                   Eval      ToCSZ = %trimr(JBTOCITY) + ', ' +
0062.00 C                             %trimr(JBTOSTATE) + ' ' +
0063.00 C                             %trimr(JBTOZIP5)
0064.00 C                   Eval      FullName = %trimr(JBFRMFIRST)
0065.00 C     FullName      Cat       JBFRMINIT:1   FullName
0066.00 C     FullName      Cat       JBFRMLAST:1   FullName
0067.00 C                   Eval      *In51 = JBFRMPH1 > 0
0068.00 C                   Eval      *In52 = JBFRMPH2 > 0
0069.00 C                   Eval      *In53 = JBFRMPCELL > 0
0070.00 C                   ExFmt     JOBREC
...

Compile the program, run the app from VS. You should see something like Figure 27:

Addresses
Fig. 27 Name and Addresses Panel

4.3 Adding a Map

We don't want the driver to get lost, so we will now add a map to show the delivery route in the Job Record. Mobile RPG provides a very useful construct for adding a Google Map to an app, the DdsGMap control. The DdsGMap presents the user with a Google Map containing optional markers, routes, and terrain views. To the RPG program, the DdsGMap presents itself as a Subfile. Each record of the subfile represents an address to be included in the map. You can view the exact subfile representation of the DdsGMap (and other) controls if you save the DDS when exporting. The DdsGMap control has several properties that name the Subfile Control, the Subfile, the Address Field in the subfile record format, and the Clear Indicator to clear the subfile.

Start by adding a DdsGMap control into the third panel, replacing the placeholder text (Map) we added earlier. In the Properties window, set the SubfileControlName to JobAddrSfc, the SubfileName to JobAddrSfl, the AddressField to FullAddr, AddressFieldLength to 128, and ClearIndicator to indicator 97. With this, we just declared a one-field subfile and subfile control in the JobRec record format. For the cosmetic part of the map, set MapType to Roadmap, Marks to Route, and Height to 200px. We learned earlier that we can add grouping text to a panel, so add Suggested Route as grouping text to the parent panel of the DdsGMap:

<mdf:DdsPanel ID="DdsPanel4" runat="server" CssClass="DdsPanel" GroupingText="Suggested Route">
    <mdf:DdsGMap ID="DdsGMap1" runat="server" AddressField="FullAddr" AddressFieldLength="128" ClearIndicator="97" SubfileControlName="JobAddrSfc" SubfileName="JobAddrSfl" Marks="Route" Height="200px" />
</mdf:DdsPanel>

Build and Export the page. In the RPG source, we need to specify the use of the subfile and declare the RRN field. After that, write a subroutine, LoadMap, that clears the subfile control and writes the To and From addresses into the subfile:

0001.00 FCAROMAIN  CF   E             WORKSTN Handler('MOBILERPG')
0002.00 F                                     Sfile(JobAddrSfl:JobAddrRrn)
...
0006.00 DJobAddrRrn       S              4  0
...
0073.00 C                   Eval      *In53 = JBFRMCELL > 0
0074.00 C                   ExSr      LoadMap
0075.00 C                   ExFmt     JOBREC
...
0093.00 C     LoadMap       BegSr
0094.00 C                   SetOn                                        97
0095.00 C                   Write     JobAddrSfc
0096.00 C                   Eval      FullAddr = %trim(JBFRMSTR1) + ' ' +
0097.00 C                             %trimr(JBFRMSTR2) + ', ' +
0098.00 C                             %trimr(JBFRMCITY) + ' ' +
0099.00 C                             %trimr(JBFRMSTATE) + ' ' +
0100.00 C                             %trimr(JBFRMZIP5)
0101.00 C                   Eval      JobAddrRrn = 1
0102.00 C                   Write     JobAddrSfl
0103.00 C                   Eval      FullAddr = %trimr(JBTOSTR1) + ' ' +
0104.00 C                             %trimr(JBTOSTR2) + ', ' +
0105.00 C                             %trimr(JBTOCITY) + ' ' +
0106.00 C                             %trimr(JBTOSTATE) + ' ' +
0107.00 C                             %trimr(JBTOZIP5)
0108.00 C                   Eval      JobAddrRrn = 2
0109.00 C                   Write     JobAddrSfl
0110.00 C                   Eval      *In97 = *off
0111.00 C                   Write     JobAddrSfc
0112.00 C                   EndSr

Compile your RPG program, and run the app from VS. You should see something like Figure 28:

Map
Fig. 28 Google Map Showing the Suggested Route

4.4 Adding Images

To complete the Job screen we need to show the user the pictures of the car at pickup time and have a way to add more pictures at pickup and delivery. To complete this section you need to install the sample pictures that ship with Mobile RPG in IFS of your IBM i. See appendix C for instructions. We will be using the DdsImage control. It is a very simple but powerful control that can display one or many images that are stored in IFS on your IBM i. Some wildcard special characters:
    '?' matches a single character
    '*' matches 0 or more characters
All it needs to be given is the path and the file name. Multiple images are supported via wildcards in the file name.

We'll start with the Origin images, and repeat the process for the Destination images. Add a DdsImage control in the fourth panel, replacing the placeholder text. Set the grouping text of the panel to At Origin. Set the DdsImage DirectoryField to ImgPath, DirectoryFieldLength to 250, NameField to ImgOrig, NameFieldLength to 32, and give it a Width of 200 and Height of 150. Repeat for the fifth panel, setting the grouping text to At Destination, and all the properties of the image to the same values except NameField, which should be ImgDest:

<mdf:DdsPanel ID="DdsPanel5" runat="server" CssClass="DdsPanel" GroupingText="At Origin">
    <mdf:DdsImage ID="DdsImage1" runat="server" DirectoryField="ImgPath" DirectoryFieldLength="250" NameField="ImgOrig" NameFieldLength="32" Width="200" Height="150" />
</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel6" runat="server" CssClass="DdsPanel" GroupingText="At Destination">
    <mdf:DdsImage ID="DdsImage2" runat="server" DirectoryField="ImgPath" DirectoryFieldLength="250" NameField="ImgDest" NameFieldLength="32" Width="200" Height="150" />
</mdf:DdsPanel>

Build and Export the page. Edit the RPG source and add code to set the fields ImgPath, ImgOrig, and ImgDest. The images are stored in the path /AsnaMrSamp/MRCarolina/VehiclePhotos/<vhid>, where<vhid> is the vehicle ID, a 4 digit number that goes from 1001 to 1020. The file names are stored as JPEG files with the pattern o-<vhid>-????.jpg for pictures at origin, and d-<vhid>-????.jpg for pictures at destination. The RPG code is then:

...
0010.00 DVPath            C                   Const('/AsnaMrSamp/MRCarolina/-
0011.00 D                                     VehiclePhotos/')
...
0077.00 C                   Eval      ImgPath = %trimr(VPath) + %trimr(VHID)
0078.00 C                   Eval      ImgOrig = 'o-' + %trimr(VHID) + '-????.jpg'
0079.00 C                   Eval      ImgDest = 'd-' + %trimr(VHID) + '-????.jpg'
...

Compile the program and run the app. You should see something like Figure 29:

Map
Fig. 29 Images at Origin

To finish these panels let's add the upload capability. Upload will only work in browsers and mobile devices that support it. We'll start with the Origin properties: set UploadCondition to *true, UploadCaption to Add Picture, UploadKey to F5, UploadedNameField to NewImgOrig, with UploadedNameFieldLength set to 32. Set the same values for Destination, except set UploadedNameField to NewImgDest.

When the control is rendered, the user will see a set of widgets on the screen that will allow him to select an image from the gallery of this device or to take a picture if the device has an integrated camera. When the user is satisfied with the chosen image, he will be able to touch or press a button to start the upload process.

The name given to the file will be generated by the DdsImage control based on the value of the field specified in NameField and reported back in the field specified in the UploadedNameField property. If the value given in NameField contains wildcards, the control will generate a new name following the pattern of the wildcard name, with random numbers used in place of the wild cards. The location in IFS of the new file will be in the field specified in the DirectoryField property.

<mdf:DdsPanel ID="DdsPanel5" runat="server" CssClass="DdsPanel" GroupingText="At Origin">
    <mdf:DdsImage ID="DdsImage1" runat="server" DirectoryField="ImgPath" DirectoryFieldLength="250" NameField="ImgOrig" NameFieldLength="32" Width="200" Height="150" UploadCaption="Add Picture" UploadCondition="*true" UploadedNameField="NewImgOrig" UploadedNameFieldLength="32" UploadKey="Enter" />
</mdf:DdsPanel>
<br />
<mdf:DdsPanel ID="DdsPanel6" runat="server" CssClass="DdsPanel" GroupingText="At Destination">
    <mdf:DdsImage ID="DdsImage2" runat="server" DirectoryField="ImgPath" DirectoryFieldLength="250" NameField="ImgDest" NameFieldLength="32" Width="200" Height="150" UploadCondition="*true" UploadCaption="Add Picture" UploadedNameField="NewImgDest" UploadedNameFieldLength="32" UploadKey="Enter" />
</mdf:DdsPanel>

Build and Export the page. Change the RPG code to detect whether a new file was uploaded to refresh the screen:

...
0081.00 C                   ExFmt     JOBREC
0082.00 C                   Select
...
0089.00 C                   When      *In05 or NewImgOrig <> *blanks
0090.00 C                              or NewImgDest <> *blanks
0091.00 C                   GoTo      ShwJob
...

Compile the RPG code and run the app. You should see something like Figure 30:

Map
Fig. 30 Uploading Capabilities

4.5 Active phone numbers.

When we display a phone number using a DdsDecField, as we did above, a mobile phone that accesses the page won't be able recognize it as such. If the user wants to call that number he would need to select, copy, and paste it into his phone dialer. What we really want is to let the phone know that the number is indeed a phone number so it will let the user dial by simply tapping or clicking. The DdsLink control provides this capability via the AppType property, shown in Figure 31:

AppType
Fig. 31 The AppType Property

As you can see from the image, there are several possible values for AppType. Here we will only explore the Telephone value. See your documentation for details on the others.

Let's change the MobileDF to support phone number dialing in the page. We'll start with Phone 1, and repeat for Phone 2 and Mobile. Add a DdsLink control replacing the JobRec_JOBFRMPH1 DdsDecField control. Set its AppType to Telephone. We need two character fields, one to hold the Text we want displayed, and one for the Url, in this case the telephone number, that we want to link to. Let's use JBFRMPH1C as the TextFieldName, and set its length to 15, and JBFRMPH1L as the UrlFieldName with a length of 10. Set its VisibleCondition to indicator 51:

<mdf:DdsLink ID="DdsLink2" runat="server" AppType="Telephone" TextFieldLength="15" TextFieldName="JBFRMPH1C" UrlFieldLength="10" UrlFieldName="JBFRMPH1L" VisibleCondition="51" />

Repeat for Phone 2 and Mobile, using indicators 52 and 53:

<mdf:DdsLink ID="DdsLink3" runat="server" AppType="Telephone" TextFieldLength="15" TextFieldName="JBFRMPH2C" UrlFieldLength="10" UrlFieldName="JBFRMPH2L" VisibleCondition="52" />
...
<mdf:DdsLink ID="DdsLink4" runat="server" AppType="Telephone" TextFieldLength="15" TextFieldName="JBFRMCELLC" UrlFieldLength="10" UrlFieldName="JBFRMCELLL" VisibleCondition="53" />

Build and Export the page. Change the RPG code to set the phone and link fields. Note that the phone field of the MobileDF record format used to be decimal but now it's character, so we need to use %EDITW to format the value:

...
0073.00 C     FullName      Cat       JBFRMLAST:1   FullName
0074.00 C                   Eval      JBFRMPH1C = %editw(JBFRMPH1:
0075.00 C                             '0(   )&   -    ')
0076.00 C                   Eval      JBFRMPH1L = %char(JBFRMPH1)
0077.00 C                   Eval      JBFRMPH2C = %editw(JBFRMPH2:
0078.00 C                             '0(   )&   -    ')
0079.00 C                   Eval      JBFRMPH2L = %char(JBFRMPH2)
0080.00 C                   Eval      JBFRMCELLC = %editw(JBFRMPCELL:
0081.00 C                             '0(   )&   -    ')
0082.00 C                   Eval      JBFRMCELLL = %char(JBFRMPCELL)
0083.00 C                   Eval      *In51 = JBFRMPH1 > 0
...

Compile the program and run the app. You should see the phone numbers now appear as clickable links and the phone will initiate a call if a phone number is tapped or clicked. See Figure 32:

Active phone link Active phone link
Fig. 32 Clickable Phone Numbers

Lesson 5 - Searching by Body and Size

It's time to create the By Body and Size screen, shown in Figure 33:

Search By Body and Size
Fig. 33 Search by Body and Size Screen

In this lesson we will:

  1. Add a Chart control.
  2. Summarize the vehicles info and display it in the chart.
  3. Respond to user taps or clicks on the various chart elements.

5.1 The DdsChart Control

The main component of this screen is the chart which shows the information in the VEHICLE file in column chart form. Vehicles are sorted by size, and within size, by body type. The user can click or tap on a column of the chart to bring up the list of vehicles corresponding to that size and body type. The Mobile RPG DdsChart control provides the support for showing summarized information in chart form. Just like the DdsGMap, the DdsChart creates a subfile so the programmer can easily load the chart with the information to be displayed.

Before we start lets explore the DdsChart control and the naming scheme it uses. Figure 34 shows a column chart with four Categories.

Chart terminology
Fig. 34 Categories and Series

Each category has two data points, with the first one belonging to the first series, Series A, and the second one to the second series, Series B. The number of Categories and Series may vary according to the application data. In Carolina we have three vehicle Categories: Compact, Medium, and Large, and three Series: Sedans, Trucks, and Vans.

Each Category in the chart represents a record in a subfile, and the fields in the subfile include the Category name, and the values of each Series for this category. Adding a new category to the chart is as simple as setting field values and writing a subfile record in the RPG program.

All of the chart titles and legends are configurable, either via a design-time constant, or with a field to be set at runtime, using the -Field/-FieldLength convention to specify the field's name and length. It's worth noting that some of these fields end up as part of the subfile control, while others end up as part of the subfile as indicated in Figure 35:

Chart terminology
Fig. 35 Field Mapping Properties

Finally, the Fonts used in the different labels and values are also configurable at design time. See Figure 36:

Chart terminology
Fig. 36 Chart Fonts

5.2 Adding the Chart

We are now ready to begin. Start by adding a new record format, set its Alias to BodySize (and its ID to _BodySize). Inside the record add a <h2> header with the text By Body and Size, and follow it with a DdsPanel to contain the chart. Use the DdsChartPanel CssClass, and set the BackColor to White, BorderColor to Black, BorderStyle to Solid, and BorderWidth to 3px.

<mdf:DdsRecord ID="_BodySize" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="BodySize">
    <h2>By Body and Size</h2>
    <mdf:DdsPanel ID="DdsPanel7" runat="server" CssClass="DdsChartPanel" BorderStyle="Solid" BorderWidth="3px" BorderColor="Black" BackColor="White">
    </mdf:DdsPanel>
</mdf:DdsRecord>

Add a DdsChart inside the panel. Let's start with the subfile control part. Set SubfileControlName to ByBdySzSfc, set ChartTitle to Inventory, CategoryTitle to Sizes. Set the ClearIndicator to 99, ChartType to Column, LegendPosition to Right, DataLabelPosition to Above. Change Height to 160px, and Width to 290px. Set Backcolor to WhiteSmoke (Hex={F5,F5,F5}).

For the subfile part, set SubfileName to ByBdySzSfl. Set CategoryField to ChSize with a CategoryFieldLength of 10. Click the ... button next to the SeriesList property. This will bring up a window where we can define the series that will be part of the chart. See Figure 37:

Series editor
Fig. 37 Defining the Chart Series

Change the default series Color to Yellow, Legend to Sedans, DataField to Sedans, DataFieldLength to 5, and DataFieldDecimals to 0. Click Add to add a second series. Set its Color to Red, Legend to Trucks, DataField to Trucks, DataFieldLength to 5, and DataFieldDecimals to 0. Click Add again to add the last series, and set its Color to Blue, Legend to Vans, DataField to Vans, DataFieldLength to 5, and DataFieldDecimals to 0.

This is what your DdsChart should look like:

<mdf:DdsChart ID="DdsChart1" runat="server" Height="160" Width="290" CategoryTitle="Sizes" ChartTitle="Inventory" ClearIndicator="99" SubfileControlName="ByBdySzSfc" DataLabelPosition="Above" BackColor="WhiteSmoke" CategoryField="ChSize" CategoryFieldLength="10" SubfileName="ByBdySzSfl">
    <SeriesList>
        <mdf:Series Color="Yellow" DataField="Sedans" DataFieldDecimals="0" DataFieldLength="5" Legend="Sedans" />
        <mdf:Series Color="Red" DataField="Trucks" DataFieldDecimals="0" DataFieldLength="5" Legend="Trucks" />
        <mdf:Series Color="Blue" DataField="Vans" DataFieldDecimals="0" DataFieldLength="5" Legend="Vans" />
    </SeriesList>
</mdf:DdsChart>

To complete the screen we need to add the navigation bar. Add a DdsBar right before the <h2> header, with ID set to BodySize_NavBar. Add two more DdsBarSegments to complete three. In the first segment add a DdsButton with CssClass set to NavButton, AidKey set to F12, and Text set to Home. In the second segment add a <span> with class set to PanelTitle. Inside the span write the screen title Select Subset. In the last segment add a DdsButton with ButtonStyle set to Image. Set the Image property to ~/Themes/Current/Images/Refresh.png, and the AidKey to F5. Now go back to the DdsRecord and set the NavigationBarControlID to BodySize_NavBar, and AttnKeys to F5 05;F12 12;:

<mdf:DdsRecord ID="_BodySize" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="BodySize" NavigationBarControlID="BodySize_NavBar" AttnKeys="F5 05;F12 12;">
    <mdf:DdsBar ID="BodySize_NavBar" runat="server" CssClass="DdsBar">
        <mdf:DdsBarSegment ID="DdsBarSegment10" runat="server" Alignment="Left">
            <mdf:DdsButton ID="DdsButton12" runat="server" CssClass="NavButton" AidKey="F12" Text="Home" />
        </mdf:DdsBarSegment>
        <mdf:DdsBarSegment ID="DdsBarSegment11" runat="server">
            <span class="PanelTitle">Select Subset</span>
        </mdf:DdsBarSegment>
        <mdf:DdsBarSegment ID="DdsBarSegment12" runat="server" Alignment="Right">
            <mdf:DdsButton ID="DdsButton13" runat="server" ButtonStyle="Image" AidKey="F5" Image="~/Themes/Current/Images/Refresh.png" />
        </mdf:DdsBarSegment>
    </mdf:DdsBar>

The last thing we need to do is detect when the user taps the chart and, based on what was tapped, show the required subset of the vehicles. For this, the DdsChart provides one property called SelectSeriesField that defines a decimal field that will be part of the subfile control. At runtime, the field indicates which of the chart components that are part of the subfile control was tapped. And it has another property, called SelectDataField, that defines a decimal field that will be part of the subfile. At runtime, the field indicates which of the chart components that are part of the subfile was tapped. Note that this field is part of the subfile, so finding the category that was tapped is as simple as READC. These two properties have companion properties to define the Aid key that indicates the user action, SelectSeriesKey and SelectDataKey. Figure 38 shows what the value of the fields should be for the different taps the user may perform:

Tapping options
Fig. 38 Selection Properties

Let's add these fields to the chart, set SelectDataField to BdySzData, and SelectSeriesField to BdySzSries. Use F7 for the SelectDataKey, and F8 for the SelectSeriesKey. Enable both keys in the record FuncKeys, pairing them with indicators 7 and 8 respectively:

<mdf:DdsRecord ID="_BodySize" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="BodySize" NavigationBarControlID="BodySize_NavBar" AttnKeys="F5 05;F12 12;" FuncKeys="F7 07;F8 08;">
...
    <mdf:DdsChart ID="DdsChart1" runat="server" Height="160" Width="290" CategoryTitle="Sizes" ChartTitle="Inventory" ClearIndicator="99" SubfileControlName="ByBdySzSfc" DataLabelPosition="Above" BackColor="WhiteSmoke" CategoryField="ChSize" CategoryFieldLength="10" SelectDataField="BdySzData" SelectDataKey="F7" SelectSeriesField="BdySzSries" SelectSeriesKey="F8" SubfileName="ByBdySzSfl">

Build and Export the page. Now, the first task to do in the RPG code is load the chart with the summarized data. We will use another logical file, VEHJOB01, which is very similar to JOBVEH01, but with a three-part key, indexed by size (Category), then by body type (Series), and finally by vehicle ID:

A     R RVEHJOB		 JFILE(VEHICLE JOB)
A     J           		 JOIN(VEHICLE JOB)
A                 		 JFLD(VHID JBVHID)
A       VHID        10      TEXT('ID')
A       VHVIN       17      TEXT('Serial Number')
A       VHMAKE      20      TEXT('Make')
A       VHMODEL     40      TEXT('Model')
A       VHYEAR       4P 0   TEXT('Year')
A       VHPLATE     10      TEXT('Plate')
A       VHCOLOR     20      TEXT('Color')
A       VHRUNS      1       TEXT('Is drivable')
A       VHBODY      10      TEXT('Body Style')
A       VHSIZE      10      TEXT('Size')
A       JBID        10      TEXT('Job ID')
A       JBVHID      10      TEXT('Vehicle ID')
A       JBFRMFIRST  30      TEXT('From First Name')
A       JBFRMINIT    1      TEXT('From Initial')
A       JBFRMLAST   30      TEXT('From Last Name')
A       JBFRMSTR1   50      TEXT('From Street 1')
A       JBFRMSTR2   50      TEXT('From Street 2')
A       JBFRMCITY   20      TEXT('From City')
A       JBFRMSTATE   2      TEXT('From State')
A       JBFRMZIP5    5      TEXT('From Zip Code')
A       JBFRMCNTRY  30      TEXT('From Country')
A       JBFRMPH1    10P 0   TEXT('Telephone 1')
A       JBFRMPH2    10P 0   TEXT('Telephone 2')
A       JBFRMPCELL  10P 0   TEXT('Cell phone')
A       JBTOSTR1    50      TEXT('To Street 1')
A       JBTOSTR2    50      TEXT('To Street 2')
A       JBTOCITY    20      TEXT('To City')
A       JBTOSTATE    2      TEXT('To State')
A       JBTOZIP5     5      TEXT('To Zip Code')
A       JBTOCNTRY   30      TEXT('To Country')
A     K VHSIZE
A     K VHBODY
A     K VHID

We'll need a loop to iterate over all the records, in key order, to count within each vehicle size how many of each body type there are. Once we are done with a vehicle size, we have to write the subfile record. Here's the code:

0001.00 FCAROMAIN  CF   E             WORKSTN Handler('MOBILERPG')
0002.00 F                                     Sfile(JobAddrSfl:JobAddrRrn)
0003.00 F                                     Sfile(ByBdySzSfl:ByBdySzRrn)
...
0006.00 FVEHJOB01  IF   E           K DISK
...
0009.00 DByBdySzRrn       S              4  0
...
0136.00 C     BYBDYSZE      BegSr
0137.00 C     ReLdBdySz     Tag
0138.00 C                   ExSr      LdBdySzCh
0139.00 C                   ExFmt     BodySize
0140.00 C                   Select
0141.00 C                   When      *In07
0142.00 C*   Data tapped
0143.00 C                   ReadC     ByBdySzSfl
0144.00 C                   Eval      VHSIZE = CHSIZE
0145.00 C                   Select
0146.00 C                   When      BdySzData = 2
0147.00 C                   Eval      VHBODY = 'Sedan'
0148.00 C                   When      BdySzData = 3
0149.00 C                   Eval      VHBODY = 'Truck'
0150.00 C                   When      BdySzData = 4
0151.00 C                   Eval      VHBODY = 'Van'
0152.00 C                   EndSl
0153.00 C                   ExSr      BdySzSubst
0154.00 C                   When      *in08
0155.00 C*   Chart tapped
0156.00 C                   When      *In12
0157.00 C                   LeaveSr
0158.00 C                   EndSl
0159.00 C                   GoTo      ReLdBdySz
0160.00 C                   EndSr
0161.00
0162.00 C*  Load the By Body and Size Chart
0163.00 C     LdBdySzCh     BegSr
0164.00 C                   Eval      *In99 = *On
0165.00 C                   Write     ByBdySzSfc
0166.00 C                   Eval      ByBdySzRrn = 0
0167.00 C     *LOVAL        SetLL     VEHJOB01
0168.00 C                   Eval      ChSize = *Blanks
0169.00 C                   Eval      *In30 = *Off
0170.00 C                   DoW       *In30 = *Off
0171.00 C                   Read      VEHJOB01                             3030
0172.00 C                   If        VHSIZE <> CHSIZE or *In30
0173.00 C                   If        CHSIZE <> *Blanks
0174.00 C                   Eval      ByBdySzRrn += 1
0175.00 C                   Write     ByBdySzSfl
0176.00 C                   EndIf
0177.00 C                   Eval      Sedans = 0
0178.00 C                   Eval      Trucks = 0
0179.00 C                   Eval      Vans = 0
0180.00 C                   Eval      CHSIZE = VHSIZE
0181.00 C                   EndIf
0182.00 C                   Select
0183.00 C                   When      VHBODY = 'Sedan'
0184.00 C                   Eval      Sedans += 1
0185.00 C                   When      VHBODY = 'Truck'
0186.00 C                   Eval      Trucks += 1
0187.00 C                   When      VHBODY = 'Van'
0188.00 C                   Eval      Vans += 1
0189.00 C                   EndSl
0190.00 C                   EndDo
0191.00 C                   Eval      *In99 = *Off
0192.00 C                   Write     ByBdySzSfc
0193.00 C                   EndSr
0194.00
0195.00 C*  Load subset of vehicles
0196.00 C     BdySzSubst    BegSr
0197.00 C                   EndSr

Compile the RPG program and run the app. Your browser should look like Figure 39. If you are using a mobile device you can drag the tab and show the data in table form.

Search by Body and Size Search by Body and Size
Fig. 39 Searching by Body and Size

Lesson 6 - Subset List of Vehicles

When the Chart we created in Lesson 5 is tapped we need to show the vehicle Subset screen, in Figure 40, listing the subset of vehicles that was selected.

Subset of Vehicles
Fig. 40 The Vehicle Subset Screen

In this lesson we will:

  1. Use a DdsList control to display the subset of jobs based on body and size of vehicles.
  2. Respond to a user tap or click on a particular job to show the Job screen.

6.1 The DdsList Control

The main component of this screen is the list control which shows a subset of the vehicles in list form. The chevron marker > on the right of each vehicle is a clickable item that will cause the app to show the Job information for that vehicle. The Mobile RPG DdsList control provides a very easy way to construct this kind of list. Just like the DdsGMap and the DdsChart, the DdsList creates a subfile so the programmer can easily create the list with the required information.

Before we start lets explore the DdsList control and the naming scheme it uses. See Figure 41.

List Elements
Fig. 41 DdsList Components

Each item of information, which corresponds to a subfile record, is expressed with a Category, a Text, a Detail, an Image, and a Navigable field (Navigable tells whether the item responds to a user tap or click). Two more fields that are not visible are the Selected field and the Value field. Selected indicates which record appears selected when displayed on the screen, and Value is a field that can contain any arbitrary information associated with this record. Each of these parts of the list record are defined via properties of the DdsList control. If more than one record has the same Category, the DdsList control will show them grouped together.

6.2 Adding the List Control

Let's begin by adding a new DdsRecord, naming it JobList as its Alias, and _JobList as its ID. Add a DdsBar with three segments, just like the one we added in Lesson 5: set the DdsBar's ID to JobList_NavBar. Add two more DdsBarSegments to complete three. In the first segment add a DdsButton with CssClass set to NavButton, AidKey set to F12, and Text set to Back. In the second segment add a <span> with class set to PanelTitle. Inside the span write the screen title Vehicle Subset. In the last segment add a DdsButton with ButtonStyle set to Image. Set the Image property to ~/Themes/Current/Images/Refresh.png, and the AidKey to F5. Now go back to the DdsRecord and set the NavigationBarControlID to JobList_NavBar, and AttnKeys to F5 05;F12 12;:

<mdf:DdsRecord ID="_JobList" runat="server" CssClass="DdsRecord" EraseFormats="*ALL" Style="position: relative;" Alias="JobList" AttnKeys="F5 05;F12 12;" NavigationBarControlID="JobList_NavBar">
    <mdf:DdsBar ID="JobList_NavBar" runat="server" CssClass="DdsBar">
        <mdf:DdsBarSegment ID="DdsBarSegment13" runat="server" Alignment="Left">
            <mdf:DdsButton ID="DdsButton14" runat="server" CssClass="NavButton" AidKey="F12" Text="Back" />
        </mdf:DdsBarSegment>
        <mdf:DdsBarSegment ID="DdsBarSegment14" runat="server">
            <span class="PanelTitle">Vehicle Subset</span>
        </mdf:DdsBarSegment>
        <mdf:DdsBarSegment ID="DdsBarSegment15" runat="server" Alignment="Right">
            <mdf:DdsButton ID="DdsButton15" runat="server" ButtonStyle="Image" AidKey="F5" Image="~/Themes/Current/Images/Refresh.png" />
        </mdf:DdsBarSegment>
    </mdf:DdsBar>
</mdf:DdsRecord>

We are done with the navigation bar. Now add a DdsList after the DdsBar. Set its ListType to Navigation. Learn about the other list types in the Mobile RPG documentation. Set Backcolor to WhiteSmoke. Set SubfileControlName to JobListSfc, SubfileName to JobListSfl, and ClearIndicator to 90. Set CategoryField to VehCat with a CategoryFieldLength of 30, TextField to VehDesc with a TextFieldLength of 50, DetailField to VehCSZ with a DetailFieldLength of 60 and a DetailAlignment set to Below. Set the SelectedField to Selected. Set the ValueField to Cookie, with a ValueFieldLength of 30. We will use the ValueField to keep track of the record key in the VEHJOB01 logical file, which is of length 30. We'll define a data structure in the program consisting of these key fields. Set NavigableField to *true, which means that all the records in the list are navigable. Finally, set the Image related properties: ImageLocation to HostIFS, ImageDirectoryField to ImgPath, ImageDirectoryFieldLength to 250, ImageNameField to ImgName, ImageNameFieldLength to 32.

<mdf:DdsList ID="DdsList1" runat="server" CssClass="DdsList"    CssClassChevron="DdsListChevron"
   CssClassDetail="DdsListDetail" CssClassImage="DdsListImage" CssClassList="DdsListList"
   BackColor="WhiteSmoke" SubfileControlName="JobListSfc" SubfileName="JobListSfl" ClearIndicator="90"
   SelectedField="Selected" CategoryField="VehCat" CategoryFieldLength="30"
   DetailField="VehCSZ" DetailFieldLength="60"
   NavigableField="*true" TextField="VehDesc" TextFieldLength="50" ValueField="Cookie" ValueFieldLength="30"
   ImageDirectoryField="ImgPath" ImageDirectoryFieldLength="250" ImageNameField="ImgName" ImageNameFieldLength="32" />

Build and Export the page. In the RPG code we will load and write the DdsList subfile and show the Subset screen when a particular column in the By Body and Size chart is tapped or clicked. It will also show the Job screen when a particular vehicle is tapped or clicked in the list on Subset screen:

0001.00 FCAROMAIN  CF   E             WORKSTN Handler('MOBILERPG')
0002.00 F                                     Sfile(JobAddrSfl:JobAddrRrn)
0003.00 F                                     Sfile(ByBdySzSfl:ByBdySzRrn)
0004.00 F                                     Sfile(JobListSfl:JobListRrn)
...
0011.00 DJobListRrn       S              4  0
0012.00 DTotListRec       S              4  0
...
0018.00 DCookie           DS
0019.00 DVHID
0020.00 DVHSIZE
0021.00 DVHBODY
0022.00
0023.00 C     KLSzBdy       KList
0024.00 C                   KFld                    VHSIZE
0025.00 C                   KFld                    VHBODY
0026.00 C     KLSzBdyID     KList
0027.00 C                   KFld                    VHSIZE
0028.00 C                   KFld                    VHBODY
0029.00 C                   KFld                    VHID
...
0159.00 C     BYBDYSZE      BegSr
0160.00 C     *Like         Define    VHBODY        VEHBODY
0161.00 C     *Like         Define    VHSIZE        VEHSIZE
0162.00 C     ReLdBdySz     Tag
0163.00 C                   ExSr      LdBdySzCh
0164.00 C                   ExFmt     BodySize
0165.00 C                   Select
0166.00 C                   When      *In07
0167.00 C*   Data tapped
0168.00 C                   ReadC     ByBdySzSfl
0169.00 C                   Eval      VEHSIZE = CHSIZE
0170.00 C                   Select
0171.00 C                   When      BdySzData = 1
0172.00 C                   Eval      VEHBODY = *Blanks
0173.00 C                   When      BdySzData = 2
0174.00 C                   Eval      VEHBODY = 'Sedan'
0175.00 C                   When      BdySzData = 3
0176.00 C                   Eval      VEHBODY = 'Truck'
0177.00 C                   When      BdySzData = 4
0178.00 C                   Eval      VEHBODY = 'Van'
0179.00 C                   EndSl
0180.00 C                   ExSr      BdySzSubst
0181.00 C                   When      *In08
0182.00 C*   Non-data tapped - do nothing
0183.00 C                   When      *In12
0184.00 C                   LeaveSr
0185.00 C                   EndSl
0186.00 C                   GoTo      ReLdBdySz
0187.00 C                   EndSr
...
0190.00 C     LdBdySzCh     BegSr
0191.00 C                   Eval      *In99 = *On
0192.00 C                   Write     ByBdySzSfc
0193.00 C                   Eval      ByBdySzRrn = 0
0194.00 C     *LOVAL        SetLL     VEHJOB01
0195.00 C                   Eval      ChSize = *Blanks
0196.00 C                   Eval      *In30 = *Off
0197.00 C                   DoW       *In30 = *Off
0198.00 C                   Read      VEHJOB01                             3030
0199.00 C                   If        VHSIZE <> CHSIZE or *In30
0200.00 C                   If        CHSIZE <> *Blanks
0201.00 C                   Eval      ByBdySzRrn += 1
0202.00 C                   Write     ByBdySzSfl
0203.00 C                   EndIf
0204.00 C                   Eval      Sedans = 0
0205.00 C                   Eval      Trucks = 0
0206.00 C                   Eval      Vans = 0
0207.00 C                   Eval      CHSIZE = VHSIZE
0208.00 C                   EndIf
0209.00 C                   Select
0210.00 C                   When      VHBODY = 'Sedan'
0211.00 C                   Eval      Sedans += 1
0212.00 C                   When      VHBODY = 'Truck'
0213.00 C                   Eval      Trucks += 1
0214.00 C                   When      VHBODY = 'Van'
0215.00 C                   Eval      Vans += 1
0216.00 C                   EndSl
0217.00 C                   EndDo
0218.00 C                   Eval      *In99 = *Off
0219.00 C                   Write     ByBdySzSfc
0220.00 C                   EndSr
...
0231.00 C     BdySzSubst    BegSr
0232.00 C     SubsetStrt    Tag
0233.00 C                   ExSr      LdSbstSfl
0234.00 C                   Eval      TotListRec = JobListRrn
0235.00 C                   ExFmt     JobList
0236.00 C                   Select
0237.00 C                   When      *In12
0238.00 C                   LeaveSr
0239.00 C                   When      *In05
0240.00 C                   GoTo      SubsetStrt
0241.00 C                   EndSl
0242.00 C                   ReadC     JobListSfl
0243.00 C                   Eval      Selected = '0'
0244.00 C                   Update    JobListSfl
0245.00 C     NextOrPrev    Tag
0246.00 C     JobListRrn    Chain     JobListSfl                         3030
0247.00 C  N30KLSzBdyID     Chain     VEHJOB01                           3030
0248.00 C                   If        *In30
0249.00 C                   GoTo      SubsetStrt
0250.00 C                   EndIf
0251.00 C                   ExSr      SHOWJOB
0252.00 C                   Select
0253.00 C                   When      *In12
0254.00 C                   LeaveSr
0255.00 C                   When      *In07 and JobListRrn > 1
0256.00 C                   Eval      JobListRrn -= 1
0257.00 C                   When      *In08 and JobListRrn < TotListRec
0258.00 C                   Eval      JobListRrn += 1
0259.00 C                   EndSl
0260.00 C                   GoTo      NextOrPrev
0261.00 C                   EndSr
...
0264.00 C     LdSbstSfl     BegSr
0265.00 C                   Eval      *In90 = *On
0266.00 C                   Write     JobListSfc
0267.00 C                   Eval      JobListRrn = 0
0268.00 C                   Eval      VHSIZE = VEHSIZE
0269.00 C                   Eval      VHBODY = VEHBODY
0270.00 C     KLSzBdy       SetLL     VEHJOB01
0271.00 C                   Read      VEHJOB01                             3030
0272.00 C                   DoW       *In30 = *off
0273.00 C                   Eval      VehCat = %trimr(VHSIZE) + ' ' +
0274.00 C                             %trimr(VHBODY)
0275.00 C                   Eval      VehDesc = %char(VHYEAR) + ' ' +
0276.00 C                             %trimr(VHMAKE) + ' ' +
0277.00 C                             %trimr(VHMODEL)
0278.00 C                   Eval      VehCSZ = %trimr(JBFRMCITY) + ' ' +
0279.00 C                             %trimr(JBFRMSTATE) + ' ' +
0280.00 C                             %trimr(JBFRMZIP5)
0281.00 C                   Eval      ImgPath = %trimr(vPath) + %trimr(VHID)
0282.00 C                   Eval      ImgName = 'o-' + %trimr(VHID) + '-0001.jpg'
0283.00 C                   Eval      JobListRrn += 1
0284.00 C                   Write     JobListSfl
0285.00 C     KLSzBdy       ReadE     VEHJOB01                             3030
0286.00 C                   EndDo
0287.00 C                   Eval      *In90 = *Off
0288.00 C                   Write     JobListSfc
0289.00 C                   EndSr

Compile the program and run the app. You should see something like Figure 42:

By Body and Size Subset of Vehicles The Job
By body and Size, click or tap on "compact sedan" Click or tap on "2008 Dodge" Try the '<' and '>' buttons
Fig. 42 Process of Searching by Body and Size

Lesson 7 - Calling Other Programs

Introduction

As more features are added to an App it may be desirable to break the code and Markup into several smaller modules. ASNA Mobile RPG follows the IBM i RPG development model in that programs can call other programs which use their own Display Files. In this lesson we will learn how to add a new Mobile Display File and how to call another program. For this we will create the Management Dashboard screen in a new Mobile Display File, and the code to handle it in a new RPG program. See Figure 43:

Dashboard
Fig. 43 The Dashboard

In this lesson we will:

  1. Add a new Mobile DF.
  2. Use two chart controls to show summarized management info.
  3. Create a new RPG program that shows the Dashboard screen.
  4. Call the new RPG program.

7.1 - Adding the new Mobile Display File

We'll start by adding a new Mobile Display File to the application. In Solution Explorer, right-click the MobileDFs folder, select Add, and then Add New Item, as shown in Figure 44:

Add New Item
Fig. 44 Adding a New Mobile Display File

The Add New Item dialog will pop up, shown in Figure 45. On the left pane select Visual C#, on the central pane select ASNA Mobile RPG Display File. Change the aspx file name to Dashboard.aspx:

New Display File
Fig. 45 Add New Item Dialog

The new MobileDF already contains a DdsFile with one DdsRecord. The record has a navigation bar, already wired to the record, with three bar segments. Change the DdsRecord Alias to Charts, and ID to _Charts. In the navigation button that's in the first bar segment, change the AidKey to F12, and change the Text from End to Home. Add F12 linked to indicator 12 to the list of AttnKeys of the record. In the second (middle) bar segment set the title text to Dashboard. In the third bar segment add a DdsButton with ButtonStyle set to Image, AidKey to F5, and the Image to the path of the Refresh.png image. Add F5 linked to indicator 5 to the list of AttnKeys of the record:

<mdf:DdsRecord id="_Charts" runat="server" style="position: relative;" Alias="Charts"
     CssClass="DdsRecord" NavigationBarControlID="NavigationBar" FuncKeys="F3 03;" EraseFormats="*ALL" AttnKeys="F12 12;F5 05;" >

    <mdf:DdsBar ID="NavigationBar" runat="server" CssClass="DdsBar">
        <mdf:DdsBarSegment ID="DdsBarSegment1" runat="server" Alignment="Left">
            <mdf:DdsButton ID="DdsButton1" ButtonStyle="Button" runat="server" CssClass="NavButton" AidKey="F12" Text="Home" />
        </mdf:DdsBarSegment>
        <mdf:DdsBarSegment ID="DdsBarSegment2" runat="server" Alignment="Center">
            <span class="PanelTitle" >Dashboard</span>
        </mdf:DdsBarSegment>
        <mdf:DdsBarSegment ID="DdsBarSegment3" runat="server" Alignment="Right">
            <mdf:DdsButton ID="DdsButton10" runat="server" ButtonStyle="Image" AidKey="F5" Image="~/Themes/Current/Images/Refresh.png" />
        </mdf:DdsBarSegment>
    </mdf:DdsBar>

After the navigation bar, we need two charts to display the Historical Revenue and the Top 10 states in sales. Start by adding two DdsChart controls after the bar, with one line break between them. Set the first one's properties as follows:

Add two series, in the first one set DataField to Revenue, DataFieldLength to 5, and DataFieldDecimals to 0. Set the Color to Yellow, and the Legend to Revenue. In the second one set DataField and Legend both to Expense, Color to Red, and the same length and decimals as Revenue (5, 0).

For the second DdsChart, here are the properties:

Add just one series to this chart. Set its DataField to StateRev, DataFieldLength to 6, and DataFieldDecimals to 0:

<mdf:DdsChart ID="DdsChart1" runat="server" Height="160px" Width="290px" SubfileControlName="MonthSfc" SubfileName="MonthSfl" BackColor="WhiteSmoke" ChartTitle="Historical Revenue" CategoryField="MonthName" CategoryFieldLength="3" EnableDataView="False">
    <SeriesList>
        <mdf:Series DataField="Revenue" Color="Yellow" DataFieldDecimals="0" DataFieldLength="5" Legend="Revenue" />
        <mdf:Series DataField="Expense" Color="Red" DataFieldDecimals="0" DataFieldLength="5" Legend="Expense" />
    </SeriesList>
</mdf:DdsChart>
<br />
<mdf:DdsChart ID="DdsChart2" runat="server" Height="160px" Width="290px" SubfileControlName="StateSfc" SubfileName="StateSfl" BackColor="WhiteSmoke" ChartTitle="Top 10 States" ChartType="Pie" CategoryField="StateAbbr" CategoryFieldLength="2" EnableDataView="False">
    <SeriesList>
        <mdf:Series DataField="StateRev" DataFieldDecimals="0" DataFieldLength="6" />
    </SeriesList>
</mdf:DdsChart>

Build and Export the new Dashboard.aspx display file.

Because we don't have real data for these charts we will write the RPG program to load some mock data. We'll do three months of mock revenues and expenses for the bar chart, and 10 states with mock revenues for the pie chart. As we'll be creating a new source file called DASHBOARD, we need to add the CALL to it from CARORPG when the user selects the Management button:

0294.00 C     MGMNTDSH      BegSr
0295.00 C                   Call      'DASHBOARD'
0296.00 C                   EndSr

And the DASHBOARD RPG mock code is:

0001.00 FDashBoard CF   E             WORKSTN Handler('MOBILERPG')
0002.00 F                                     Sfile(MonthSfl:MonthRrn)
0003.00 F                                     Sfile(StateSfl:StateRrn)
0004.00
0005.00 DMonthRrn         S              4  0
0006.00 DStateRrn         S              4  0
0007.00
0008.00 C*********************************************************************
0009.00 C* Use the RPG-Cycle to display the Dashboard, get out on IN12
0010.00 C*********************************************************************
0011.00 C                   ExSr      LoadDash
0012.00 C                   ExFmt     Charts
0013.00 C                   If        *In12
0014.00 C                   Eval      *InLR = *On
0015.00 C                   Return
0016.00 C                   EndIf
0017.00
0018.00 C******************************************
0019.00 C* Load Dashboard
0020.00 C******************************************
0021.00 C     LoadDash      BegSr
0022.00 C                   ExSr      LoadMonth
0023.00 C                   ExSr      LoadState
0024.00 C                   EndSr
0025.00
0026.00 C******************************************
0027.00 C*   Load Month Chart Subfile
0028.00 C******************************************
0029.00 C     LoadMonth     BegSr
0030.00 C                   Eval      *In99 = *On
0031.00 C                   Write     MonthSfc
0032.00 C                   Eval      MonthName = 'Jan'
0033.00 C                   Eval      Revenue = 12345
0034.00 C                   Eval      Expense = 10345
0035.00 C                   Eval      MonthRrn = 1
0036.00 C                   Write     MonthSfl
0037.00 C                   Eval      MonthName = 'Feb'
0038.00 C                   Eval      Revenue = 11345
0039.00 C                   Eval      Expense = 09345
0040.00 C                   Eval      MonthRrn = 2
0041.00 C                   Write     MonthSfl
0042.00 C                   Eval      MonthName = 'Mar'
0043.00 C                   Eval      Revenue = 13345
0044.00 C                   Eval      Expense = 10345
0045.00 C                   Eval      MonthRrn = 3
0046.00 C                   Write     MonthSfl
0047.00 C                   Eval      *In99 = *Off
0048.00 C                   Write     MonthSfc
0049.00 C                   EndSr
0050.00
0051.00 C******************************************
0052.00 C* Load State Chart Subfile
0053.00 C******************************************
0054.00 C     LoadState     BegSr
0055.00 C                   Eval      *In99 = *On
0056.00 C                   Write     StateSfc
0057.00 C                   Eval      StateRrn = 0
0058.00 C                   Eval      StateAbbr = 'CA'
0059.00 C                   Eval      StateRev = 193640
0060.00 C                   Eval      StateRrn += 1
0061.00 C                   Write     StateSfl
0062.00 C                   Eval      StateAbbr = 'TX'
0063.00 C                   Eval      StateRev = 130743
0064.00 C                   Eval      StateRrn += 1
0065.00 C                   Write     StateSfl
0066.00 C                   Eval      StateAbbr = 'NY'
0067.00 C                   Eval      StateRev = 115650
0068.00 C                   Eval      StateRrn += 1
0069.00 C                   Write     StateSfl
0070.00 C                   Eval      StateAbbr = 'FL'
0071.00 C                   Eval      StateRev = 75400
0072.00 C                   Eval      StateRrn += 1
0073.00 C                   Write     StateSfl
0074.00 C                   Eval      StateAbbr = 'IL'
0075.00 C                   Eval      StateRev = 64420
0076.00 C                   Eval      StateRrn += 1
0077.00 C                   Write     StateSfl
0078.00 C                   Eval      StateAbbr = 'PA'
0079.00 C                   Eval      StateRev = 57560
0080.00 C                   Eval      StateRrn += 1
0081.00 C                   Write     StateSfl
0082.00 C                   Eval      StateAbbr = 'NJ'
0083.00 C                   Eval      StateRev = 49700
0084.00 C                   Eval      StateRrn += 1
0085.00 C                   Write     StateSfl
0086.00 C                   Eval      StateAbbr = 'OH'
0087.00 C                   Eval      StateRev = 48340
0088.00 C                   Eval      StateRrn += 1
0089.00 C                   Write     StateSfl
0090.00 C                   Eval      StateAbbr = 'VA'
0091.00 C                   Eval      StateRev = 42770
0092.00 C                   Eval      StateRrn += 1
0093.00 C                   Write     StateSfl
0094.00 C                   Eval      StateAbbr = 'NC'
0095.00 C                   Eval      StateRev = 40740
0096.00 C                   Eval      StateRrn += 1
0097.00 C                   Write     StateSfl
0098.00 C                   Eval      *In99 = *Off
0099.00 C                   Write     StateSfc
0100.00 C                   EndSr

Compile both RPG programs and run the app. Figure 46 shows what you should see when selecting Management Dashboard on the Home Menu screen:

Dashboard
Fig. 46 The Management Dashboard

Afterword

Here ends this Tutorial. You made it this far, Congratulations! You have written a non-trivial Mobile RPG application. By completing this exercise you now have the basic tools and knowledge to design and create your own MR apps. From here, your journey should continue with a deeper exploration of the different controls and their options; they enrich the presentation of your apps. Also, you should read about designing mobile apps. There are many online references. One of the most important design ideas you should always keep in mind is Don't Make Me Think. A user who blankly stares at your screen while trying to figure out what he needs to do to get what he wants from the app is a frustrated user who will end up jumping ship at the first chance.

One subject this tutorial didn't touch is Cascading Style Sheets, or CSS. Try to learn some CSS; play by tweaking the style sheets in the Mobile RPG template, see what happens. Take for instance NavButton in Theme.css:

.NavButton, .NavButtonIcon, .NavButtonSymbol {
    width: auto;
    height: 2.8em;
    margin-top: 0.2em;
    padding: 0 5px;
    overflow: hidden;
    line-height: 28px;
    font-family: inherit;
    font-size: 0.8em;
    font-weight: bold;
    text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0;
    text-overflow: ellipsis;
    text-decoration: none;
    text-align: center;
    white-space: nowrap;
    color: #fff;
    background:  rgb(37,141,200); /* Old browsers. VS Designer */
    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #8ea4c1), color-stop(0.5, #5877a2), color-stop(0.5, #476999), color-stop(1, #4a6c9b));
    background-image: -ms-linear-gradient(top, #B5C1C0 0%, #12A1F3 50%, #1230EF 100%);
    -webkit-border-radius: 6px;
    border-radius:6px;
    border: 1px solid #45505d;
    vertical-align:middle;
}

what does it all mean? can you change the blueish gradient color for a reddish one, so it matches the navigation bar?

We hope you put all these tools to good use. Happy coding!



Appendix

A - Creating a Database Name

Database Names are created using DataGate Explorer within VS. Open VS and go to the DATAGATE menu, select DataGate Explorer. A tool window will show up that looks something like Figure 47:

DataGate Explorer
Fig. 47 DataGate Explorer

The thumbtack at the top bar helps in pinning the window so it doesn't autohide. Right-click the Database Names folder, and select "Add New Database Name". See Figure 48:

New DB Name
Fig. 48 Adding a New Database Name

The dialog in Figure 49 will come up:

New DB Name Dialog
Fig. 49 New Database Name Dialog

where you need to enter the following:

If the server components for Mobile RPG and DataGate 400 were installed to listen to port 5042, there is nothing more to do. Press "Test Connection" to verify the given parameters can connect successfully. If the ASNA server components are listening on a different port you need to set the correct port number in the Advanced properties dialog. Ensure that Test Connection returns successfully before pressing OK. Back in the DataGate Explorer Window you will see Figure 50:

Give it a name
Fig. 50 New Database Name

Change "DB2" to a more appropriate name, like the name of the IBM i server on the network. Any name is fine, e.g. "My400_5042". This is the name to be used when using ASNA products and a Database Name is required.

B - Setting up your Mobile app in IIS

This appendix assumes you have IIS installed on your developer machine, and that you have WiFi in your offices that allows your WiFi device into your local network.

  1. Log on to your developer computer as an administrator.
  2. Open the Control Panel, then open Administrative Tools.
  3. Then, open Internet Services Manager, and right-click on Default Web Site. You'll get a screen like Figure 51:
    IIS Management Console
    Fig. 51 IIS Manager
  4. Select Add Application. A dialog, shown in Figure 52, will popup. In the Alias box type in "Carolina", then type in or browse for the path where you created your website. If you used VS default values when creating the website, it should be something like C:\Users\xxxx\Documents\Visual Studio 2015\WebSites\Carolina
    Add Application
    Fig. 52 Adding an IIS Application
  5. Open Windows explorer and navigate to the website path (e.g. C:\Users\xxxx\Documents\Visual Studio 2015\WebSites\Carolina). In the folder properties, Figure 53, share the folder with "Everyone", with Read/Write permissions:
    Share folder
    Fig. 53 Sharing the App Folder
  6. Verify that your computer firewall allows http (World Wide Web) connections to port 80 on your machine. Check with your system administrator.
  7. Go back to the IIS Manager window. Test that the Carolina website works by right-clicking on it, selecting Manage Application > Browse. You should see your default browser open on the first screen of your Carolina app.
  8. Set your phone to use your local WiFi and open your phone browser. Type in the URL for the website you created. Let's say in your domain your computer is called Zip-win8, then the URL is Zip-win8/Carolina. Press GO and you should see the first page of your app in your phone.
  9. If you want you can add it on the phone's Home Screen so it becomes like a native app. When you run it, it will be in full-screen mode, as shown in Figure 54:
    Share folder
    Fig. 54 Setting a Home Screen App

C - Restoring Carolina's Photo File Structure

This appendix assumes you have already installed the ASNAMRSAMP sample library in your IBM i. It's easier if you run two 5250 emulator sessions, one of them in qsh.

  1. Look for the CAROPHOTOS save file inside the ASNAMRSAMP library.
  2. In qsh, create the /AsnaMrSamp/MrCarolina directory
    mkdir /AsnaMrSamp/MrCarolina
  3. Restore the CAROPHOTOS save file:
    RST DEV('/qsys.lib/asnamrE80.lib/carophotos.file') OBJ('/AsnaMrSamp/MrCarolina')
  4. In qsh, verify that the restored files and file structure are correct.
    > pwd
       /AsnaMrSamp/MRCarolina/VehiclePhotos
       $
    > ls
       1001 1004 1007 1010 1013 1016 1019
       1002 1005 1008 1011 1014 1017 1020
       1003 1006 1009 1012 1015 1018
       $

    There are twenty folders under /AsnaMrSamp/MRCarolina/VehiclePhotos each named using the vehicle ID in the Carolina VEHICLE file. Inside each of these folders, there are JPEG photos with the naming convention
    <prefix>-<VHID>-<unique-seq> .jpg
    Where
    <prefix> may be either 'o' (for origin) or 'd' (for destination).
    <VHID> is the Vehicle ID.
    <unique-seq> is a four-digit unique number.
    For example, Vehicle ID 1001 at origin would have pictures named o-1001-0001.jpg, o-1001-0002.jpg, etc.