Making Checkboxes Sticky

Learn how to keep checkboxes updated for users after submitting a form when they return to it.

By Bob Ray  |  December 15, 2021  |  3 min read
Making Checkboxes Sticky

In the previous article, we saw how to use JavaScript to check or clear all checkboxes in a form with a button. In that article, I mentioned in that the placeholders in the form were there for the purpose of making the checkboxes “sticky”. When users come back to the page, the checkboxes will be checked as they were the last time the form was submitted.

In this article, we’ll take a look at how to make that happen.

Overview

The basic method is to get the checkbox settings from the post and write them somewhere safe whenever the form is submitted. When the form is displayed, PHP code gets the checkbox settings (if any) from that location and replaces the placeholders accordingly.

The solution described here is for MODX Revolution, but it could be used elsewhere by storing the form Template in a file and the checkbox settings in another file.

Here’s a review of the form code, which in the case of MODX is stored as a Tpl Chunk called MyFormTpl.

The Form

<div class="my_form_form_div">
    <form id="my_form_form" method="post">
        <fieldset id="my_form_fieldset" style="width:20%">
            <div id="checkbox_div"> ### Select the tasks to be Performed <input type="checkbox" name="my_form_tasks[]" [[!+checkResourcesChecked]] value="checkResources" /> Check Resources <input type="checkbox" name="my_form_tasks[]" [[!+checkSnippetsChecked]] value="checkSnippets"> Check Snippets <input type="checkbox" name="my_form_tasks[]" [[!+checkPluginsChecked]] value="checkPlugins"> Check Plugins <input type="button" class="my_form-check-button" onClick="checkAll(true);" value="Check All" />
                <input type="button" class="my_form-check-button" onClick="checkAll(false);" value="Clear All" />
                <input type="hidden" name="submitVar" value="MySubmitVar">
            </div>
            <br class="clear" />
            <input type="checkbox" name="my_form_verbose" value="verbose"> Verbose Output <br class="clear" />
            <div class="form-button">
                <input type="submit" id="my_form_submit" name="my_form_do_tasks" value="Perform tasks" />
            </div>
        </fieldset>
    </form>
</div>

The first three input tags contain MODX placeholder tags. Since we’re going to replace those placeholder tags using str_replace(), they could take another form, though they do need some unique token at the beginning and the end. These tags will be replaced either with nothing, or with checked depending on the current default settings for each checkbox.

In the old days, people used checked="checked" to be valid HTML, every tag attribute must have a value specified, but this is no longer true (and even when it was, most browsers would handle a naked checked correctly).

The PHP Code

In the Overview above, I mentioned that the settings for the checkboxes were going to be stored somewhere safe, but where? They could go in a file, in a custom table in the database, in a Template Variable, in a System Setting, or somewhere else. I chose to put them in the default properties of the Snippet (in this example, called MySnippet).

That makes them easy to find and set manually and very easy to retrieve in code, since MODX will be sending them automatically in the $scriptProperties array. They’re simply a comma-separated list in the value of the defaultTests property. That means you could also put them in the Snippet tag if you wanted to set a particular set of defaults in a specific situation.

The value of the Snippet property, default_tests will contain just a comma-separated list of names for checkboxes that should be checked, without the enclosing tokens, and without the “Checked” suffix, like this: checkResources,checkPlugins. We’ll add the suffix in the replacement code.

The comments in the following code explain what’s going on:

<?php

/* MySnippet snippet */

/* Check to see if the form has been submitted */

if (!isset($_POST['submitVar'])
    || ($_POST['submitVar'] != 'MySubmitVar')) {

    /* Not a submission -- get the defaults
     * from the properties */
    $defaults = $modx->getOption('defaultTests',
        $scriptProperties, '');

    /* Convert comma-separated list to an array */
    $defaults = explode(',', $defaults);

    /* Get the Form Chunk */
    $chunk = $modx->getChunk('MyFormTpl');

    /* Do the replacements */
    foreach ($defaults as $default) {
        /* Sanitize the value by removing
            everything but alphabet characters */
        $default = preg_replace("/[^A-Za-z]/", '',
            $default);

        /* Replace the corresponding placeholder */
        $chunk = str_replace('[[!+' . trim($default) .
        'Checked]]', 'checked', $chunk);
    }
    /* Return the finished form for display */
    return $chunk;

} else {
    /* Form has been Submitted */

    /* Get the checkbox settings (if any) from the $_POST */
    if (isset($_POST['my_form_tasks'])
        && is_array($_POST['my_form_tasks'])
    ) {

        foreach ($_POST['my_form_tasks'] as $k => $test) {
            /* Checkbox is checked */
            $properties[$test] = true;

            /* Sanitize the post value */
            $_POST['my_form_tasks'][$k] =
                preg_replace("/[^A-Za-z]/", '', $test);
        }

        /* Convert the array to a comma-separated list */
        $defaults = implode(',', $_POST['my_form_tasks']);
    } else {
        /* No checkboxes checked. Save an empty string */
        $defaults = '';
    }

    /* Save the defaults in the snippet's defaultTest
     *  property to make them sticky */

    /* Get the snippet object */
    $snippet = $modx->getObject('modSnippet',
        array('name' => 'MySnippet'));

    /* Make sure we have it */
    if (!$snippet) {
        die('Could not find MySnippet snippet');
    }
    /* Get the snippet's default properties
    $props = $snippet->getProperties();

    /* Set the defaultTests array  member */
    $props['defaultTests'] = $defaults;

    /* Set the properties and save the snippet */
    $snippet->setProperties($props, true);
    $snippet->save();

    /* Perform the regular operations for a submission
     * here.
     *
     * If the form will be redisplayed, repeat the
     * code from the section above for getting the
     * chunk, but use the $_POST values for the
     * replacements rather than the $scriptProperties
     * before returning the form. */
}

Notes on the Code

The code and comments should be fairly obvious, but let’s look more closely at a few things.

First, and most important, the setProperties() call must have true for its second argument. That tells MODX to merge your properties and their field values with the existing ones. Without that, any other properties of the Snippet will have their field values wiped out or set to the default values. Yes/No fields will become text fields, descriptions will be lost, and values may be transformed into something that will no longer work.

Even if your property is the only one the Snippet has, it’s still necessary to send the true argument. Otherwise a number of the property fields will be wiped out because getProperties() just gets the keys and values, leaving out all the other property fields.

Notice that the Verbose checkbox in the form has no placeholder tag, so it won’t be sticky. It will always be unchecked when the form is first displayed. There may be options you never want to have checked by default (e.g., Delete Database). You could make the Verbose checkbox sticky by simply inserting a placeholder for it.

The placeholders are very carefully named. Each of them must match what is in the value attribute of the input tag plus the word Checked (with a capital 'C'). The replacement code uses this to replace the correct placeholder:

/* Replace the corresponding placeholder */
$chunk = str_replace('[[!+' . trim($default) .
    'Checked]]', 'checked', $chunk);

We’re looping through the default checkbox settings, one by one. In the example, if all three options were checked, the settings would be checkResources, checkSnippets, checkPlugins. When we get to the checkPlugins setting, the search string in the first argument to str_replace() would be [[!+checkPluginsChecked]]. The replacement value would be checked. So the Check Plugins checkbox would be set to checked.

Notice that in the HTML form code, each placeholder as a space on both sides. This is critical. Without it, the checked attribute won’t be recognized as an attribute.

The trim() function is called on each default value in case a user has manually edited the default settings and left one or more spaces before or after one of the settings.

Any placeholders that are not listed in the defaults won’t be touched by the code. Since no actual placeholders are set, MODX will remove those placeholders before the form is sent to the browser and they will be unchecked. If you are using this method outside of MODX, need want to replace them manually with an empty string if the checkbox should be unchecked, otherwise, they’ll show up in the form.

What About Upgrades?

It may have occurred to you that if we used this method with a MODX Extra like FormIt, our saved checkbox defaults might be overwritten if the Extra is upgraded. That’s true, but the damage would only affect the very first visitor to the form. Once the form has been submitted, the defaults will be set. If this is an issue, or if you want to hide the defaults from certain users, you can always save the defaults somewhere else. A custom System Setting called SiteCheckDefaultTests would be a good choice. The getOption() call would still get the default settings, but the code to save it would look like this:

$setting = $modx->getObject('modSystemSetting',
    array('key' => 'SiteCheckDefaultTests'));
$setting->set('value', $defaults);
$setting->save();

Other Uses

You could use a similar technique if you don’t want the checkbox default settings to be sticky, but do want to be able to set them somewhere. You could hard-code the checked attribute in the form, but if you have a lot of checkboxes, it might be more convenient to use a comma-separated list that could be easily edited. In this case, you’d just remove the code that saves the settings and makes them sticky.


Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.