Step 6 - Capturing Form Data Using Templates

Ok, now that we know how to look at data, let's capture data and save it to disk.

Html Creation with Templates

At this point we've done everything in code including HTML creation, which is not really optimal. For simple things, and highly dynamic situations writing out your HTML in code is OK, but in most cases this gets too tedious and is not very flexible as everytime you make a change you have to change your code and recompile and re-deploy your application.

Web Connection supports a number of different mechanisms for generating HTML using external markup templates:

  • Templates
    Templates are evaluated pages that can contain FoxPro expressions and simple code blocks. Templates are lightweight and are completely evaluated in memory at runtime. They are easily changed and can be updated while the application is running

  • Scripts
    Scripts are similar to templates, but are actually compiled into full FoxPro programs and executed. Scripts turn markup into an actual PRG file that is executed. Because scripts are real code you can do more including using structured statements (IF,SCAN,DO WHILE,FOR etc.), but because the code compiles they are not as easy to update as scripts when multiple instances are running.

Template Example

For this example, let's stick to templates as they are the easiest solution and they're easy to work with.

Templates are basically HTML with FoxPro expressions embedded inside of them. You use a Process class method as we have been doing, but rather than writing the HTML in code you can set up a model - variables or tables - that you pass in to the template, which then actually renders the output.

Why show this now? Without templates creating an input form for editing customers, the HTML gets very lengthy and we don't want to write and maintain it all in code. Templates and sripts allow offloading of this HTML to separate text files that can be updated independently of the code.

The following is the full HTML for a EditCustomer.wp template we'll use to edit a customer:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Edit Customer</title>
    ...
    </style>
</head>
<body>
    <div class="banner">
       ...
    </div>


    <div id="MainView">
        <div class="container">
            <a href="customerlist.wp" class="btn btn-success btn-sm pull-right" style="margin-top: 20px;">
                <i class="fa fa-arrow-left"></i> Customer List
            </a>

            <h3>
                <i class='fa fa-edit'></i> Edit Customer
            </h3>
            <hr/>
            
            <!-- Conditionally display an error message  -->
            
                <div class="alert alert-warning">
                    <i class="fa fa-warning error"></i>
                     asd
                </div>
            

            <form action="editcustomer.wp" method="POST"
                  class="form-horizontal container" 
                  style="padding: 0 15px 30px;">

                <div class="form-group">
                    <label class="col-sm-2">Company:</label>
                    <div class="col-sm-7">
                        <input name="Company" value="" 
                               class="form-control" 
                               placeholder="Enter a company name" />
                    </div>
                </div>

                <div class="form-group form-horizontal">
                    <label class="col-sm-2">First Name:</label>
                    <div class="col-sm-7">
                        <input name="FirstName" value=""
                               class="form-control"
                               placeholder="Enter the first name"
                               />
                    </div>
                </div>

                <div class="form-group form-horizontal">
                    <label class="col-sm-2">Last Name:</label>
                    <div class="col-sm-7">
                        <input name="LastName" value=""
                               class="form-control"
                               placeholder="Enter the last name" />
                    </div>
                </div>

                <div class="form-group form-horizontal">
                    <label class="col-sm-2">Address:</label>
                    <div class="col-sm-7">
                         <textarea name="Address" 
                                  class="form-control"
                                  placeholder="Enter the full address"
                            ></textarea>
                    </div>
                </div>

                <div class="form-group form-horizontal">
                    <label class="col-sm-2">Email:</label>
                    <div class="col-sm-7">
                        <input name="Email" value=""
                               class="form-control"
                               placeholder="Enter the email address"
                               />
                    </div>
                </div>

                <div class="form-group form-horizontal">
                    <label class="col-sm-2">Billing Rate:</label>
                    <div class="col-sm-7">
                        <input name="BillRate" value=""
                               class="form-control"
                               placeholder="Enter the billing rate in dollars"/>
                    </div>
                </div>

                <div class="form-group form-horizontal">
                    <label for="Entered" class="col-sm-2">Entered:</label>
                    <div class="col-sm-10">
                        <div class="input-group" id="Entered" style="width: 150px;">
                            
                            <span class="input-group-addon">
                                <i class="fa fa-calendar"></i>
                            </span>
                        </div>
                        
                    </div>
                </div>

                <hr/>

                <button type="submit" name="btnSubmit" class="btn btn-primary">
                    <i class="fa fa-check"></i> Save Customer
                </button>
                
                <input type="hidden" name="id" value="" />
            </form>
        </div> <!-- container -->
    </div> <!-- end #MainView -->

    ... 
    
    <!-- date time picker related scripts -->
    <script src="~/bower_components/jquery/dist/jquery.min.js"></script>
    
    <link rel="stylesheet" href="~/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" />
    <script src="~/bower_components/moment/min/moment.min.js"></script>
    <script src="~/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js"></script>

    <script>
    $("#Entered").datetimepicker({
    	format: "MM/DD/YYYY"
    });
    </script>
</body>
</html>

As mentioned templates can be full HTML documents and that's what this page is. The key content of this template is the middle section contained in the <form> tag. You can see the form fields embedded in there with each looking something like this:

<div class="form-group">
    <label class="col-sm-2">Company:</label>
    <div class="col-sm-7">
        <input name="Company" value="" 
               class="form-control" 
               placeholder="Enter a company name" />
    </div>
</div>

Notice the < %= poCustomer.Company % > embedded in the value. This assigns the Company value from a FoxPro object that is in scope and writes it into the Value attribute so that it displays in the page when the page renders.

The simple display version of this page without editing logic looks like this:

FUNCTION EditCustomer()

lcId = Request.Params("Id")

IF !USED("Customers")
   USE Customers IN 0
ENDIF
SELECT Customers

IF !EMPTY(lcId)
   LOCATE FOR Id=lcId
ELSE
   GO BOTTOM 
   SKIP 
ENDIF

PRIVATE poCustomer
SCATTER NAME poCustomer Memo


*** Render EditCustomer.wp
Response.ExpandTemplate()

ENDFUNC

When this page is accessed in the browser with:

http://localhost/WebDemo/EditCustomer.wp?id=_4FG12Y7U6

you get a form that renders the customer data like this:

The key to this code is the call to Response.ExpandTemplate() which renders reads the EditCustomer.wp template from disk and then parses the template with the values from the FoxPro environment. Specifically the properties of poCustomer are merged into the template to produce the field output.

ExpandTemplate() also takes optional parameters that allows you to specify the filename for the template. The default, parameterless call we use essentially does the following:

Response.ExpandTemplate(Request.GetPhysicalPath() + "EditCustomer.wp")

Self Posting Pages

This template also is set up as a self-posting page. What this means is that the display and postback page are the same and a single method handles both.

You want for most if not all input forms, so that if an error occurs you can display the error in the page along side the controls in question.

So let's adjust the EditCustomer method in the WebProcess class to handle saving the data posted when we click the Save Customer button.

FUNCTION EditCustomer()

lcId = Request.Params("Id")

IF !USED("Customers")
   USE Customers IN 0
ENDIF
SELECT Customers

IF !EMPTY(lcId)
   LOCATE FOR Id=lcId
ELSE
   GO BOTTOM 
   SKIP 
ENDIF

PRIVATE poCustomer, pcErrorMsg
pcErrorMsg = ""
SCATTER NAME poCustomer Memo

IF Request.IsPostBack()
   poCustomer.Id = Request.Form("id")
   poCustomer.Company = Request.Form("Company")
   poCustomer.LastName = Request.Form("LastName")
   poCustomer.FirstName = Request.Form("FirstName")
   poCustomer.Address = Request.Form("Address")
   poCustomer.Email = Request.Form("Email")
   poCustomer.BillRate = VAL(Request.Form("BillRate"))
   poCustomer.Entered = CTOT(Request.Form("Entered"))
   
   *** Validation goes here     
   
   *** No id = new customer
   IF EMPTY(poCustomer.Id)
      poCustomer.Id = SYS(2015)
      APPEND blank
   ENDIF
   
   GATHER NAME poCustomer MEMO 
   
   pcErrorMsg = "Customer info saved."   	
   Response.AppendHeader("refresh","3;url=CustomerList.wp")
ENDIF

*** Render EditCustomer.wp
Response.ExpandTemplate()

ENDFUNC

The code is pretty straight forward. If we come in with an HTTP GET operation - that is we're just coming to the page initially we simply display the data. The Request.IsPostBack() check fails and the form is simply displayed with the value given.

When we press the Save button, the form is submitted and now Request.IsPostBack() is true and so the form data is retrieved. Request.Form() retrieves form variables in the POST buffer of the request which are sent via IIS to Web Connection. The values are always strings, so in the case of the BillRate and Entered fields those values have to be converted to their respective types that get stored into the database.

This form works with existing Customers if you pass in an ID. If you don't pass an id an empty form pops up and when the customer is saved a new customer is created.

This is obviously an overly simple example that doesn't take into account error checking or even providing all the fields for editing here, but it gives you an idea of how forms and user input are handled. You can obviously use HTML form fields for many more things such as asking the user for query parameters or other information that you can then use in your code to perform business operations on. Request.Form() provides you with user input just as a VFP form field would in a standalone application.


Step 7 - Dynamic Editing with Templates


© West Wind Technologies, 1996-2017 • Updated: 02/25/16
Comment or report problem with topic