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
Hmmm:
https://webconnection.west-wind.com/docs/_sap14y146.htm#template-example
First example in that section.
+++ Rick ---
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
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.
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 --
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
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 ---
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>© 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
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 ---
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
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.
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
HTML is text, nothing more. You can use VFP if you want, like modify file editcustomer.wp
.
Personally I prefer Visual Studio Code for my HTML. And you can't complain about the price, it's free.
Also Rick recommends Visual Studio Code, see Not using Visual Studio? No Problem
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
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.
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
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.
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
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 ---
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
John,
I'm having similar issues that you are having but may have figured out some of the issues with some of the incorrect entries in the help (and believe me, having written Help, I know how that can happen).
Full disclaimer, I'm not a Westwind expert, comfortable with earlier versions (5.0) but very much a beginner with > 7.0 (I updated to 7.10)
So to your issues:
- First, in the first code snippet of Step 7 the first two references to loErrors should be poErrors.
- Second, in the third-to-the-last code snippet at the bottom of Step 7, the reference to Entered_Field should just be Field
- Third, the reason why your result page looks very bland and not the way they're shown in the help is you need to write out the Header and Footer. This is what I put at the very bottom of the WebProcess.EditCustomer.wp method:
*** Write Out Header
Response.Write(this.PageHeaderTemplate("Edit Customer"))
*** Render EditCustomer.wp
Response.ExpandTemplate()
*** Write Out Footer
Response.Write(this.PageFooterTemplate())
So assuming you were able to get the basic test web page up, the below link has my full WebDemo directory and below, with an enhanced Default.htm web page with a link to CustomerList.wp method in the Customer Name column, and updated WebDemoMain.prg, WebProcess.prg files and the EditCustomer.wp file itself, I think everything is working, along with links from the CustomerList to go directly into the EditCustomer.wp page. This has my full WebDemo directory. WebDemo Up to Step 7
Unless I'm missing something, the references to the wwBusiness class isn't introduced into the next tutorials, so I haven't dealt with that here.
I'm going to post separately the errors I'm still dealing with and that is a "duplicate" error notification at the top of the page and also a non-working datepicker field.
Hopefully that gets you through Step 7 with the exceptions to be noted separately.
Maybe we can work through this together and provide feedback to Rick to help with the Help, because it does get frustrating when you're trying to follow the detailed instructions, including copying code, and things just don't work (and believe me, I've done that sometimes to some of my customers, as well).
Thanks, Kevin
Hi Kevin and thank you. I did finally get thru step 7. Basically I just kept reading it over until I "got it" with regard to the meaning (some reading between the lines). A couple weeks ago, I began going thru the steps using the business objects. I understand business objects because I use Visual FoxExpress as a framework for all my VFP projects. I was really just trying to learn WebConnect and that seemed the next step to doing so. I got totally frustrated mostly because I am trying to "understand" what Rick's help is showing me but with all the HTML and all the terms like DOM, JSON, GET, POST, Response, Request, jQuery, JavaScript, Binding, Bootstrap DML, MVC (the list is long), I was constantly searching Google for terms meanings and was just not able to get thru the steps because of my lack of understanding soooo many things. I stopped and found an online course for $12.00 that contains about 500 short videos on becoming a web developer. I am on video 270 and have learned A LOT!. Actually I am just about to get back to Rick's step by steps today as I believe I have gotten from the video course what I needed. This may help you or someone else. I have learned that a browser is really like a FoxPro runtime and that there are 3 pieces to web pages, HTML for the skeleton, CSS for the adjectives or maybe how the skeleton is displayed and JavaScript for the programming language. Funny, when the video course got into explaining if...endif's and for loops, I quickly realized what JavaScript was (in my world it is FoxPro for web sites or browsers). I have been off this project for a few days but hope to get back into it today and make some real progress understanding Rick's walk throughs. I would be happy to share with you. Give me a day or 2 to get my feet back on the ground with regard to all this and I will do another reply. Thanks again, John
Thanks, John.
Would you mind sending me that link for the web tutorial you're going through? You can email me privately at krhunt@jwhcorp.com
Kevin, because it may help others as well. The url is https://www.udemy.com/courses/search/?src=ukw&q=The%20Web%20Developer%20Bootcamp%20colt%20steele
I did notice the price went up from $11.99 to $13.99 but still a bargain.
John
Thanks Kevin,
With 7.10 out the door the docs are the next step but it'll take time. The setup docs have been updated, but the walk-throughs are still lagging behind. I need to go through all of them and actually run/create the examples one at a time and go through again.
+++ Rick ---