ColdFusion Contest Entry Examined - Part 5 Welcome to (yet another) entry in the ColdFusion contest. This is turning into a much bigger series than I imagined, but there is so much to learn from looking at how people solve problems. Are people getting bored yet? Check out the last entry which also contains links to all the old entries as well.
Our new entry can be viewed here. The code consists of two files.
contest1.cfm:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Guess My Number Contest</title>
</head>
<body> <cfoutput> <form action="contest2.cfm" method="post"> Please, pick a number between 1 and 100 <BR> Your number: <input size="3" maxlength="3" type="text" name="test_number"> <input type="hidden" name="right_number" value="#RandRange(1,100)#"> <input type="hidden" name="total_times" value="1"> <input type="submit" name="GO" value="GO"> </form> </cfoutput> </body> </html>
contest2.cfm:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Guess My Number Contest</title>
</head>
<body>
<!-- Is this the number in the good range--> <cfif #form.test_number# GT 100 OR #form.test_number# LT 1> <cfoutput> Your number is not within 1 and 100<BR> <cfset total_times_added = #form.total_times#> <form action="contest2.cfm" method="post"> Please, pick a number between 1 and 100 <BR> Your number: <input size="3" maxlength="3" type="text" name="test_number"> <input type="hidden" name="right_number" value="#form.right_number#"> <input type="hidden" name="total_times" value="#total_times_added#"> <input type="submit" name="GO" value="GO"> </form> </cfoutput> <cfelse>
<!-- Is this the right number --> <cfif form.test_number IS form.right_number> You got it in <cfoutput> #form.total_times# </cfoutput>times. Congratulations!<BR> Wanna play <a href="contest1.cfm">again</a> ??? </cfif>
<!-- Is this the number too high--> <cfif #form.test_number# GT #form.right_number#> <cfoutput> #form.test_number# is too high, please try again<BR> <cfset total_times_added = #form.total_times# + 1> <form action="contest2.cfm" method="post"> Your number: <input size="3" maxlength="3" type="text" name="test_number"> <input type="hidden" name="right_number" value="#form.right_number#"> <input type="hidden" name="total_times" value="#total_times_added#"> <input type="submit" name="GO" value="GO"> </form> </cfoutput> </cfif>
<!-- Is this the number too low--> <cfif form.test_number LT form.right_number> <cfoutput> #form.test_number# is too low, please try again<BR> <cfset total_times_added = #form.total_times# + 1> <form action="contest2.cfm" method="post"> Your number: <input size="3" maxlength="3" type="text" name="test_number"> <input type="hidden" name="right_number" value="#form.right_number#"> <input type="hidden" name="total_times" value="#total_times_added#"> <input type="submit" name="GO" value="GO"> </form> </cfoutput> </cfif> </cfif> </body>
</html>
I think this code sample is an excellent example of a topic I wanted to bring up at a later time - organization, specifically, organization of a form. ColdFusion is a simple language. One of the primary examples of how easy it is can be demonstrated by examining a simple form. What isn't so obvious is how one should set up a form to handle the display of the form, the validation of the form, and finally the result of the successful completion of the form.
Not to be too mean, but I think this code sample shows a perfect example of how not to do things. Let's start with contest1.cfm. This file displays the initial form and posts to contest2. After that, the file isn't used again until you finish the game.
Now look in contest2.cfm. Notice that for the scenario where the user guesses too high or too low, the entire form is duplicated. The same applies for a case where the number was below 1 or above 100. Taken together, the same form is written four times. As you can imagine, when the designer comes in and hands you the spec sheet, you are in trouble.
The primary solution for this is self-posting forms. All this is means is that your form posts to itself, instead of some other file. Here is a pseudo-code example of what I mean:
if(form was submitted) {
do a bunch of processing
was the form ok?
email someone, or store to db, or both
}
was there an error?
show it
show the form
This isn't terribly complex, but does prevent a lot of the problems the submission above creates. It also makes it a bit simpler to handle since you are dealing with one file, not two. (Of course, some validation may be in outside sources, like CFCs, but let's not worry about that right now.)
Outside of that - there isn't much else to complain about. He does forget to go all the way with validation, but I've made that complaint about all the other entries (and I make that mistake myself). I did notice a few extra uses of # signs where he didn't need them:
<cfif #form.test_number# GT 100 OR #form.test_number# LT 1>
This could be:
<cfif form.test_number GT 100 OR form.test_number LT 1>
The basic rule to remember is - if you aren't in a cfoutput, you typically don't need the pound signs. The only other time you will need them is inside quotes, for example:
<cfset formalName = "Mr. #form.name#">
But even that could be:
<cfset formalName = "Mr. " & form.name>
Archived Comments
Last one? Where is mines? :)
Did I say it was the last one? If so, I didn't mean it. I've got 2 more that I know of.
"Check out the last entry which ..."
:)
Right, last as in _previous_, not final. ;)
i have two comments: First, Thank God you're doing a series that examines how to do simple stuff, but do it right... I can hardly wait to see some "Here's the full-blown Ray Camden would do it this way". The second comment is I don't think I could take the criticism but I'm sure glad other people can because I don't have any formal training and I'd be making some of the very same mistakes without some solid instruction such as CFWACK and blogs like yours.
The only thing I would say is that 1 and 100 should not be a guess. We know that 1 is the low number and 100 is the high number. You should guess a number between these two. If you specified something different in your rules for the contest then I apologize. On this example though the first time I ran the test the number turned out to be 100. This is just me throwing my two cents in so don’t yell at me :)
Michael White; Actually, I wasn't planning on solving it too. ;)
Dan - Nah, I disagee. It should include 1 and 100 in the possible answers. At least thats my opinion.
Another option to avoid replicating the form is to put the form code in a separate file and use cfinclude to include it as needed. I think this could be a valid solution to solve the "code replication" problem in this example.
I am not a fan of self-submitting forms. In my experience, they can make the form overly complex because there is a lot of different code for different purposes, plus additional conditional logic to figure out what the "state" the page is in.
In this example, we'd only have two 'states' so it isn't so bad. I've often seen all the "CRUD" code in a single form. I've seen mult-step forms implemented all in a single page. Things like that can get rather confusing.
I like pages to have a single purpose, and will often separate the "form input" page from the "form processing" page.
I know a lot of people don't agree with me on this, though. Web Forms in ASP.NET are a prime example. Fusebox also uses a "submit onto myself" technique. The use of code encapsulation (CFCs, UDFs, Custom Tags) can help to greatly simplify the processing code
Ray, when you get that advanced contest rolling, email me okay? Because I keep forgetting to visit this site regularly.
Matt, if you subscribe to the RSS, you won't need to check the site again.