Web Connection
WebConnection Tutorial - How create EditCustomer.wp
Gravatar is a globally recognized avatar based on your email address. WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  All
  Jan 28, 2020 @ 07:59am

Rick,

I am trying to get thru the web connection step by step: getting started tutorial. I am in Step 6: capturing form data using templates. You show the html that goes in EditCustomer.wp but you tell nothing about how to create this template or where to put it (or if you did it went right over my little brain).

Thanks, John

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Rick Strahl
  Jan 29, 2020 @ 02:08pm

OK. I got a little further in Step 6. Here is my EditCustomer code from webprocess.prg

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

Here is my code from the page that I created in visual studio named editcustomer.wp:

<!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  -->
            <% if !Empty(pcErrorMsg) %>
                <div class="alert alert-warning">
                    <i class="fa fa-warning error"></i>
                    <%= pcErrorMsg %> asd
                </div>
            <% endif %>

            <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="<%= poCustomer.Company %>" 
                               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="<%= poCustomer.FirstName %>"
                               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="<%= poCustomer.FirstName %>"
                               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"
                            ><%= poCustomer.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="<%= poCustomer.Email %>"
                               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="<%= poCustomer.BillRate %>"
                               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;">
                            <%= HtmlTextBox("Entered_field",poCustomer.Entered,[class="form-control"])  %>
                            <span class="input-group-addon">
                                <i class="fa fa-calendar"></i>
                            </span>
                        </div>
                        <%= HtmlBindingError("Entered",poErrors) %>
                    </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="<%= poCustomer.id %>" />
            </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>

And here is what I see when I go to: localhost/WebDemo/EditCustomer.wp?id=_4FG12Y7U6

Can you tell me where I am going wrong?

Thanks, John

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  Tore Bleken
  VFPJCOOK
  Jan 30, 2020 @ 02:25am

I don't think you have done anything wrong, I think the documentation isn't correct. poErrors is defined and instantiated in step 7, so just ignore the error and continue.

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  Rick Strahl
  VFPJCOOK
  Jan 30, 2020 @ 02:14pm

Hmmm...

Looks to me like the declaration for the poErrors is there (in Diplaying a Customer EditForm first code snippet), but the code for the broken expression should be:

<%= HtmlBindingError("Entered",poError.Errors) %>

poError.Errors not poError.

Actually - checking I see that HtmlBindingError accepts either, so that can't be it. Make sure that the declaration is there in your process class code.

Looking at your code more closely I see you're not actually using the code that was provided in the sample and you are definitely missing the poError declaration so that is the problem.

Also, you don't have to do all the manual bindings and unbindings which can be very error prone especially when you need to do type conversions.

All of this:

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"))

You'll have problems with that code if BillRate or Entered are empty for example. Or if the values entered aren't numbers or dates that can be converted.

You should really use the binder for this which handles the type conversions, error handling and - if you have them configured any validations.

IF (Request.IsPostBack())
   *** this replaces your code
   poError.Errors = Request.UnbindFormVars(loCustBus.oData)

   *** Validation
   IF !loCustBus.Validate()
       poError.Errors.AddErrors( loCustBus.oValidationErrors )
   ENDIF
   
   *** Binding Error Handling
   IF poError.Errors.Count < 1 
       IF !loCustBus.Save() 
	      poError.Message = loCustBus.cErrorMsg      
       ENDIF  
   ENDIF

   IF (poError.Errors.Count > 0)
       poError.Message = poError.Errors.ToHtml()
   	   poError.Header = "Please fix the following form entry errors"
   ELSE
       poError.Message = "Customer saved."
       poError.Icon = "info"

       *** display a message then reload to the list page
       Response.AppendHeader("Refresh","2;url=customerlist.ctd")
   ENDIF               
ENDIF

It's good to understand how 'manual' unbinding works if you need to pick out individual values, but if you are binding forms with more than a handful values the binder will save you a ton of work that you otherwise have to do by hand. Reliable data assignment from input form values is not trivial especially not in bulk.

+++ Rick --

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Rick Strahl
  Jan 30, 2020 @ 03:13pm

Thank you Rick and Tore. I am not being concise enough about exactly what my issue is. In the documentation you show the form should look like the following (it's all pretty):

but what I am seeing is this (Not so pretty):

and I am needing to know why what I am seeing is "not so pretty".

Thanks, John

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  Rick Strahl
  VFPJCOOK
  Jan 30, 2020 @ 03:23pm

You're not using the _layoutpage.wcs master page that loads in all the dependencies for styles and scripts.

You removed this:

<% pcPageTitle = "Edit Customer - Customer Demo Sample" %>
<% Layout="/views/_layoutpage.wcs" %>

and added a standard HTML header that's not including all the dependencies that are usually in the header of _layoutpage.wcs

Alternately you can use the Web Connection Template Page template in Visual Studio as opposed to Web Connection Layout Content Template Page. The former creates a standalone page, while the second creates a content page. Both should generate links to all the CSS resources.

If you didn't use the templates, you can create a new page and just copy the headers from the template into your HTML.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Rick Strahl
  Feb 10, 2020 @ 03:35pm

Ok, I finally got a chance to try this again.

I deleted my editcustomer.wp and added a new file in visual studio named editcustomer.wp and used the "Web Connection Template Page" to create this file. The following is the code that visual studio generated in editcustomer.wp

<% * VS Addin Comment: SourceFile="~\..\deploy\YOUR_PROCESS_CLASS.PRG" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>editcustomer</title>

    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <meta name="description" content="" />

    <link rel="shortcut icon" href="~/favicon.ico" type="image/x-icon" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <link rel="apple-touch-icon" href="touch-icon.png" />

    <link rel="icon" href="~/touch-icon.png" />
    <meta name="msapplication-TileImage" content="~/touch-icon.png" />

    <link href="~/lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />    
    <link href="~/lib/fontawesome/css/all.min.css" rel="stylesheet" />
    <link href="~/css/application.css" rel="stylesheet" />
</head>
<body>
    <div class="banner">
    	<!-- Slideout Menu Toggle - Hamburger menu -->
        <a class="slide-menu-toggle-open no-slide-menu"
           title="More...">
            <i class="fas fa-bars    "></i>
        </a>

        <!-- Icon and Company Logo -->
        <div class="title-bar no-slide-menu">
            <a href="~/">
                <img class="title-bar-icon" src="~/images/Icon.png" />
                <div style="float: left; margin: 4px 5px; line-height: 1.0">
                    <i style="color: #0092d0; font-size: 0.9em; font-weight: bold;">Logo1</i><br />
                    <i style="color: whitesmoke; font-size: 1.65em; font-weight: 600;">Logo2</i>
                </div>
            </a>
        </div>

        <!-- top right nav menu - .hidable for options that hide on small sizes -->
        <nav class="banner-menu-top float-right">            
            <a href="#" class="hidable">
                <i class="fa fa-book"></i>
                Link
            </a>
            <a href="~/">
                <i class="fas fa-home"></i>                
                Home
            </a>            
        </nav>
    </div>


    <div id="MainView">
        <div class="container">        

            <div class="page-header-text"><i class="fa fa-list-alt"></i> editcustomer</div>
            <p>
                Content here..zzz....
            </p>


        </div>  <!-- end .container -->
    </div> <!-- end #MainView -->


    <footer>
        <a href="http://www.west-wind.com/" class="float-right">
            <img src="~/images/WestwindText.png" />
        </a>
        <small>&copy; Company, <%= YEAR(Date()) %></small>
    </footer>



    <!-- slide in menu - Remove if you don't use it --> 
  	<nav class="slide-menu">
        <div style="padding: 10px 10px 10px 3px;">

            <a class="disabled">
                <i class="fa fa-home"></i>
                Main Menu
            </a>

            <a href="~/">
                <i class="fa fa-home"></i>
                Home
            </a>

            <a href="#" class="indented">
                <i class="fas fa-newspaper-o"></i>
                Sub Link
            </a>
            <a href="#" class="indented">
                <i class="fas fa-newspaper-o"></i>
                Sub Link 2
            </a>

            <a href="#">
                <i class="fa fa-unlock-alt"></i>
                Login
            </a>
        </div>
    </nav>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>

    <!--
        Add these only if you use Bootstrap dropdowns or modals
        And if you do: don't add here, only to content pages that actually need it
        <% section="scripts" %>
                <script src="~/lib/popper.js/dist/popper.min.js"></script>
                <script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
        <% endsection %>
    -->
    <script src="~/lib/popper.js/dist/popper.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
    <script>
        // toggle menu, handle menu click and outside click to close
        $(document).on("click",
            ".slide-menu-toggle-open,.slide-menu-toggle-close," +
            ".slide-menu a, #SamplesLink,.slide-menu",
            function () {
                $(".slide-menu").toggleClass("active");
            });
    </script>    

</body>
</html>

I made one modification so it says Content here..zzz.... so I could ensure this is what I am seeing when I run it.

My editcustomer function in webprocess.prg 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, 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

and here is what I see when I run this: I know I am completely missing the boat here but where did the fields containing my customer go?

Thanks, john

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  Rick Strahl
  VFPJCOOK
  Feb 11, 2020 @ 10:19pm

I know I am completely missing the boat here but where did the fields containing my customer go?

I'm not sure I understand. You created a new page, put your dummy text there and that's what you see. That's what should be there.

The input fields have to be added to the page as templated HTML... look in the tutorial for the template that displays and/or edits the data.

As I mentioned previously you should probably use a Web Connection Template Content Page rather than than Web Connection HTML Page. This will remove all the HTML boiler plate code and just leave you with the content area to fill. Every page that is a Content Page won't have to re-create the full HTML layout. It uses the Views\_layoutpage.wcs for the 'page wrapper' template into which your content page is loaded.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Rick Strahl
  Feb 12, 2020 @ 04:47am

Thanks Rick,

I replaced the code in my body section with the body code from Step 6 where it says "The following is the full HTML for a EditCustomer.wp template we'll use to edit a customer:" and was able to get the form for entering data to display. It has an error I believe related to the datepicker but at least conceptually I did get it accomplished. The problem is I have no real idea how all that is working. I have been thru the videos at least 30 times. I can pretty much recite what you are going to say before you say it. I don't know if you are aware but in order to do what I have been having trouble with here, I have to switch between the step thru tutorial and watching the video to get pieces from each and then do a lot of reading between the lines. The bottom line here is that it is taking far too long for me to make progress on this so I will likely need some tutoring. I am going to put together an email showing exactly what I need to accomplish with Web Connection. I have one web page I need to develop and hopefully in doing so I can learn better how this works. The real complexity will be accessing my database which is the same database accessed by my Inmate Trust Fund app (my VFE app). I believe I will need to find a way to incorporate Web Connection into VFE so that when I create a new VFE project, it loads all the prerequisites for Web Connection. I will send that email asap and ask about your status for some tutoring.

Thanks, John

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  Tore Bleken
  VFPJCOOK
  Feb 12, 2020 @ 05:02am

John, I suggest that you don't focus too much on the videos. The videos are for version 6, the difference between 6 and 7 are relatively big. I can tell you that I have watched the videos 30 times also, so I understand your frustration. 😃

Especially if you watch the older Web Control Framework videos, you will be confused, since these controls are not supported anymore. I notice that Rick has removed these videos, but the link is still there.

Instead, focus on the written tutorial, since it is always 99% up to date.

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Tore Bleken
  Feb 12, 2020 @ 10:57am

Hi Tore and thank you. I have taken your advice and started over and am going thru the walkthrough. I am in Step 6. The walkthrough says "The following is the full HTML for a EditCustomer.wp template we'll use to edit a customer:" and it shows code that I assume I could put into a html document named editcustomer.wp that I would create using visual studio. It does not say exactly how I would create this document in visual studio or otherwise. I have never written any HTML code and I am not real familiar with Visual Studio so how do I produce this html file in my WebDemo project?

Thanks, John

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Tore Bleken
  Feb 12, 2020 @ 11:20am

Ok, I understand and I created editcustomer.wp

In Step 6, directly below the code for editcustomer.wp it says "The simple display version of this page without editing logic looks like this:"

That sentence is confusing to me. It sounds like it is saying something like "to display the customer record without any editing functionality" which is of course not what I want to do.

My question is: Do I add the Function EditCustomer exactly as shown in Step 6 to my WebProcess.prg?

Thanks, John

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  Tore Bleken
  VFPJCOOK
  Feb 12, 2020 @ 12:05pm

As far as I can see, yes.

Please note that I haven't gone through these tutorials myself in a very long time, and I didn't study this article thoroughly.

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Tore Bleken
  Feb 12, 2020 @ 01:04pm

Well, I am trying to determine where I am going sideways or the walkthrough is telling me wrong.

I did add the editcustomer function to my webprocess.prg and when I attempt to access http://localhost/WebDemo/EditCustomer.wp?id=_4FG12Y7U6 like Step 6 says to do, I get an error from VFP saying variable pcErrorMsg does not exist. I can see that pcErrorMsg is referenced in editcustomer.wp but I am trying to follow the walkthrough exactly and I should not be getting this error so at this point I believe the walkthrough has a bug.

Thanks, John

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  Tore Bleken
  VFPJCOOK
  Feb 12, 2020 @ 01:10pm

If I have the time, I will create a new project and follow the tutorial tomorrow. I need to refresh myself, so this is a good opportunity.

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Tore Bleken
  Feb 12, 2020 @ 01:25pm

Tore, That would be very much appreciated when you have time. I want to stick to the walkthrough exactly and try to get all the way thru Step 7 so that I comprehend.

Thanks again, john

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  Rick Strahl
  VFPJCOOK
  Feb 12, 2020 @ 01:34pm

John,

The walkthough is updated occasionally for new features and improvements, so it's possible there are small bugs like var naming or perhaps a missing reference in the walk throughs. That's not great, but it happens due to the changing nature of the material.

The idea isn't for you to cut and paste every example as is, but actually go through the steps yourself and at that point you can step through the code and should be able to see the typos.

I need to update those walk throughs from scratch again as I have to every few years. I know a number of changes were added for 7.x because of the new project system and especially for the startup steps of running the server etc. which is much simpler now.

I'll take a look at Step 6 and see whether there are problems and let you know.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: WebConnection Tutorial - How create EditCustomer.wp
  VFPJCOOK
  Rick Strahl
  Feb 13, 2020 @ 03:25pm

Rick, Aside from all that for just a minute. I am reading the topic How Web Connection Works and subtopic The Server Architecture. I am down to where it says "Instantiate the wwDemo class (the .wcs extension)" but I'm not really following what you mean. Can you elaborate a bit on what you mean?

Thanks, John

© 1996-2020