Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

davebaker

by davebaker (Pilgrim)
on Sep 23, 2002 at 21:48 UTC ( [id://200239]=user: print w/replies, xml ) Need Help??


Posts by davebaker
Edge case for checkboxes when used to update data records in Meditations
3 direct replies — Read more / Contribute
by davebaker
on Sep 20, 2024 at 15:56

    I spent many hours trying to solve a problem, found a solution, and would like to share it in case others might encounter it.

    A checkbox that appears on a form on an HTML page has a "name" attribute. Typically it has a "value" attribute. (If there is no "value" attribute, the default is for the web browser to send the string "on", I think, if the user has checked the checkbox before submitting the form.)

    Anyway, if it unchecked, naturally there is no value sent to the script that's receiving the submitted form data. Let's say the HTML in the form states that the name of the checkbox is "member." If the user is a member of a certain civic organization, as explained elsewhere on the web page, the web page asks the user to so advise the website owner by checking the checkbox. If not a member, then the user just leaves the checkbox unchecked.

    The interesting part is that the script receiving the form data has no idea whether there was a a checkbox named "member" unless the checkbox had been checked by the user. If it was left unchecked, the script doesn't get a "&member=''" (empty string) or even a "&member" in the submitted query string.

    Let's say the script uses the submitted data to add the user to a database. Probably a record in a table in a relational database. The script can be written to assume that the person is not to be recorded as a "member" of the civic organization if there is no value coming in for the "member" parameter. If so, the script inserts a record in the table, puts the empty string (or NULL) into the record's "member" field (here, the field in the table happens to be the same as the name of the form parameter), and life is good.

    But let's say the new user is indeed a member of the civic organization, and said so by checking the checkbox. So the script stores a "1" in the member field of the new record.

    OK. Next step: provide for the editing of the record. We want to have the script create an editing form that looks like the new-user form. The script pulls the user's record from the database, sees there is a value in the member field, and hence the form is created to include "checked=CHECKED" as an attribute, e.g., <input type="checkbox" name="member" checked=CHECKED> is somewhere in the form.

    But it's been six months since the user registered as a new user, and during that time she decided to let her membership in the civic organization lapse. So she wants to use the editing screen to update her record -- to literally uncheck the "member" checkbox. She does so, and submits the form.

    The script, because it doesn't receive any information about the member checkbox from the user's web browser (see above), has to ASSUME that a parameter named member was in the form, and that the failure to find any submitted value associated with that parameter, or find the parameter in the first place, means the user is wanting to have her record in the database updated so as to replace "1" with "0" (or the empty string, etc.). OK, no big deal. The code is fairly simple.

    But what if there were a different form presented to the user. One that is designed to let her change only her username, for example, and doesn't include the "member" checkbox. The HTML in the form is written to submit the data to the same script. But because the script has been written to assume that no incoming data for "member" means the person is no longer a member, then there's trouble when the script updates her record to change the "1" to "0" in the member field. The user only wanted to change her username.

    OK, so we revise the script to make no such assumption. Now, though, how will the script know when to change the "member" field from "1" to "0"? It can't assume that every form has given the user a "member" checkbox. This is the assumption that's been made in every code example that I've seen, though.

    The solution seems to be to have the form include a hidden input field that lists the names of all of the checkboxes in the form, so the script can safely assume that they're to be treated as having been submitted and they're to be treated as if the user intended them to be unchecked when the form was submitted. This can be done by printing a <input type=hidden value="member"> line somewhere inside the <form ...> and </form> tags.

    I used the CGI.pm module to create a form, used its checkbox() function to print a checkbox inside it, and found that, lo and behold, the HTML includes a line that says something like <input type="hidden" name=".cgiparams" value="member"> just before the </form> tag. I had successfully reinvented the wheel.

    CGI.pm is then able to redisplay the form (if, for example, the user supplied invalid data in the form such that the script is written to redisplay the form along with an error message), and the stickiness aspect of its created checkboxes means that a checkbox that initially was displayed as being checked will be correctly redisplayed as being unchecked if the user had in fact unchecked the checkbox before submitting the form. CGI.pm apparently knows that the checkbox it creates upon redisplay of the form is not to be checked this time, because it finds the member parameter in the .cgiparams list that was part of the submitted form and it determines that no value came in for the member parameter (or that the parameter didn't come in at all).

    My strategy in generating web forms has been to take advantage of CGI.pm's sticky checkboxes and to use HTML::Template for templates. (I don't want to use CGI.pm to create and display the entire form using the 1995-ish technique of $q->start_html; $q->start_form [etc.]; $q->end_form; $q->end_html; in the script, even if that seems to have been a good and novel idea back in the day.)

    So my script creates a sticky checkbox by calling $q->checkbox( -name => "member" ). CGI.pm returns a string of HTML that can be sent over to the template, where there is a <TMPL_VAR MEMBER_CHECKBOX> embedded in its text, waiting to receive the HTML for the checkbox, which might be checked due to CGI.pm's stickiness, or might not be.

    Alas, the sticky feature still wasn't working. A "view source" revealed that the magic <input type="hidden" name=".cgiparams" value="member"> wasn't part of the form in the generated web page. But, of course, why should it be? I hadn't sent it over to the template. But where is CGI.pm generating that string? How do I get it?

    It turns out that the string containing the hidden list of fields is prepended to </form> when CGI.pm's $q->end_form() is called. But beware (ask me how I know), this is the case only if $q->start_form() has been called before $q->checkbox() was called. it's not enough that the CGI module was instantiated. My template had its own <form action="https://yaddayadda.com/script.cgi"> and its own </form> tag, so I hadn't thought of using needing to call CGI->start_form() or CGI->end_form(). I only needed the creation of the sticky checkboxes.

    What wasn't intuitive to me, and might be useful to others, is that I needed to have my script actually call something like my $throwaway_string = $q->start_form(); somewhere before $template->param( MEMBER_CHECKBOX => $q->checkbox(-name => "member") ); and then needed to send over the list of hidden fields by doing something like my $hidden_list_of_fields_and_closing_form_tag = $q->end_form(); -- after the last checkbox or any other form field had been created by using a CGI.pm function -- followed by something like $template->param( HIDDEN_LIST_OF_FIELDS_AND_CLOSING_FORM_TAG => $hidden_list_of_fields_and_closing_form_tag );

    I don't need to send over the results of start_form(); I just need to have called it. The sticky checkboxes work even with the <form action=...> that's hard-coded in my template. But apparently I do need to call start_form() in order for CGI.pm to start paying attention and keeping a list of the names of the checkboxes (or other HTML form elements) that it's thereafter creating, so that $q->end_form() knows what to return in the way of a hidden list of fields.

    I know that a web application platform might handle this particular edge case behind the scenes, but I'm not sure. I've done some development with Dancer2, but even that fine platform seems to assume that the developer has picked and implemented some Perl module (ideally, one that's more recent and more sophisticated than CGI.pm) to handle the creation of form elements, and to somehow handle this checkbox edge case.


    Edited for clarity, probably unsuccessfully due to author's inability to communicate precisely :-)

New free TTF font for coding: Camingo in Perl News
3 direct replies — Read more / Contribute
by davebaker
on May 12, 2014 at 11:54
    I am enjoying a new monospaced font for coding my Perl using Textpad in Windows; it's called Camingo and it's available for free at myfonts.com, where you can see a demo. It has a very clear distinction between "1" and"l", a slashed zero, and a clean look I find very pleasing. The designer/publisher -- Jan Fromm -- calls it a "type family for programmers." I think they'd be OK with my pasting in this excerpt from its description:
    A compact appearance and a moderate line height provide for good legibility and make writing code a pleasure. It is based on CamingoMono, but has several specially-drawn glyphs that work well at small sizes, including Q, l, &, *, ~ and #. The stroke weights are relatively light, which makes CamingoCode suitable for both bright and dark visual themes.
'Best Sample Perl Scripts' Posted on Microsoft Site in Perl News
4 direct replies — Read more / Contribute
by davebaker
on Jul 15, 2005 at 11:55
    Lookie -- I had never seen this before --

    The Script Center Script Repository: Sample Perl Scripts

    "The Script Repository categorizes the best sample scripts designed to run on Windows 2000, Windows XP, and Windows Server 2003. The categories listed below connect you to sample scripts written using Perl for Windows." [See http://www.microsoft.com/technet/scriptcenter/scripts/perl/default.mspx.]

    [On another page, Microsoft recommends use of the ActiveState Perl distribution.]

    "And if you’d like to see all the Perl scripts, without having to click through all the categories, then visit the Master Index page for Perl scripts" [which is at] http://www.microsoft.com/technet/scriptcenter/scripts/perl/prlindex.mspx

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (2)
As of 2025-02-08 18:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Which URL do you most often use to access this site?












    Results (95 votes). Check out past polls.