Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-Page Form Submit

I have an issue that I can't seem to navigate my way through.

To start, I have a website that was built by a professional, and we no longer have a working relationship with that company. I'm managing the site myself now. I'm capable, but I am in no way an experienced web developer.

Background: We have an application procedure that uses a multipage form presented to the end user. The form is presented in 7 steps, but it's all done from one php file, using (I think) jquery / javascript to cycle through the steps, and validate some fields. In the final step, a summary is presented for the user to submit. This works beautifully.

Below is what I believe to be the relevant javascript that handles the page cycling:

<script>
$(function () {

    window.confirmLeave = true;

    $('.datefield').datepicker();

    var cache = {};                                                                                             // caching inputs for the visited steps

    $("#appForm").bind("step_shown", function(event,data){  

         if(data.isLastStep){                                                                                      // if this is the last step...then
                $("#summaryContainer").empty();                                                                     // empty the container holding the 
                $.each(data.activatedSteps, function(i, id){                                                        // for each of the activated steps...do
                    if(id === "summary") return;                                                                    // if it is the summary page then just return
                    cache[id] = $("#" + id).find(".input");                                                         // else, find the div:s with class="input" and cache them with a key equal to the current step id
                    cache[id].detach().appendTo('#summaryContainer').show().find(":input").removeAttr("disabled");  // detach the cached inputs and append them to the summary container, also show and enable them
                });
            }else if(data.previousStep === "summary"){                                                              // if we are movin back from the summary page
                $.each(cache, function(id, inputs){                                                                 // for each of the keys in the cache...do
                    var i = inputs.detach().appendTo("#" + id).find(":input");                                      // put the input divs back into their normal step
                    if(id === data.currentStep){                                                                    // (we are moving back from the summary page so...) if enable inputs on the current step
                         i.removeAttr("disabled");
                    }else{                                                                                          // disable the inputs on the rest of the steps
                        i.attr("disabled","disabled");
                    }
                });
                cache = {};                                                                                         // empty the cache again
            }
        });

</script>

I've also included the html of the form below:

<form name="appForm" id="appForm" action="submit-app-exec.php" method="post" 
enctype="multipart/form-data" autocomplete="off" onSubmit="showProgressBar()">

<fieldset class="step" id="page_1">
<div class="input">
<?php include("add-company/step1.html"); ?>
</div>
</fieldset>

<fieldset class="step" id="page_2">
<div class="input">
<?php include("add-company/step2.html"); ?>
</div>
</fieldset>

<fieldset class="step" id="page_3">
<div class="input">
<?php include("add-company/step3.html"); ?>
</div>
</fieldset>

<fieldset class="step" id="page_4">
<div class="input">
<?php include("add-company/step4.html"); ?>
</div>
</fieldset>    

<fieldset class="step" id="page_5">
<div class="input">
<?php include("add-company/step5.html"); ?>
</div>
</fieldset>

<fieldset class="step" id="page_6">
<div class="input">
<?php include("add-company/step6.html"); ?>
</div>
</fieldset>

<fieldset class="step" id="page_7">
<div class="input">
<?php include("add-company/step7.html"); ?>
</div>
</fieldset>

<fieldset class="step" id="summary" >
    <span class="font_normal_07em_black">Summary page</span><br />
    <p>Please verify your information below.</p>
    <div id="summaryContainer"></div>
</fieldset>

<div id="wizardNavigation">

    <button class="navigation_button" onclick="javascript:saveApp()">Save</button>
    <input class="navigation_button" id="back" value="Back" type="reset" />
    <input class="navigation_button" id="next" value="Next" type="submit" onclick="javascript:noSaveApp()" />

    <div class="clearFix"></div>
</div>

When the page is loaded, each field set has additional class and style attributes:

class="step ui-formwizard-content ui-helper-reset ui-corner-all" style="display: none;"

During the process, I can watch with firebug, and see that the display: none; cycles through and changes to "block" when interacting with that fieldset.

The issue: We did not build in any way for the user to save progress and complete later. I'm attempting to do this now. I have successfully created the "save" button, which triggers a javascript to change the action of the form, which POSTS the data to a new php file that handles and processes the POST data into MySQL. This works, however, the POST only passes the data from the currently viewed fieldset, instead of POSTing all the data. And I cannot figure out how to make sure all the form data is POSTed. Any guidance or suggestion would be helpful. Thanks.

EDIT:

I was able to get the correct page to load with the following:

$(function(){ $('#appForm').formwizard('show','" . $row["current_step"] . "'); }); 

This loads up the correct page. The issue now, is that the final step of this is a summary page that shows all input elements for final submission. However, it only seems to display elements from the pages that were viewed, I'm pretty sure it's the array "data.activatedSteps" that determines whether or not the elements are dispalyed in the final summary. Would your code address this better than mine? Thanks again for your help with this. –

like image 840
Brian Randolph Avatar asked Oct 14 '13 17:10

Brian Randolph


People also ask

What is multistep form?

A multi-step form is a long form that is broken into multiple pieces. They're used to make long forms, such as shipping or registration forms, less intimidating and daunting. By allowing customers and leads to complete their information in smaller chunks, you create a positive user experience and increase conversions.

Which is a benefit of using multi step forms?

You can configure a multi-step form to help users complete complicated tasks. For example, instead of having a single, confusing online order form, configure a multi-step form. The multi-step form presents information from the online order form in multiple focused and concise screens.


1 Answers

<input> objects with the property 'disable' do not get returned in a post/get.

This is the javascript that is important to pay attention to in your question.

if(id === data.currentStep){                                                                    // (we are moving back from the summary page so...) if enable inputs on the current step
    i.removeAttr("disabled");
}else{                                                                                          // disable the inputs on the rest of the steps
    i.attr("disabled","disabled");
}

This is how the previous coder is controlling what the users can edit. If you want all the values sent back when the user hits your save button first

removeAttr("disabled");

from everything on the page then submit the post/get.

It looks like the previous coder accomplished this by going through all the data on the page and making it not disabled here.

if(data.isLastStep){                                                                                    // if this is the last step...then
    $("#summaryContainer").empty();                                                                     // empty the container holding the 
    $.each(data.activatedSteps, function(i, id){                                                        // for each of the activated steps...do
        if(id === "summary") return;                                                                    // if it is the summary page then just return
        cache[id] = $("#" + id).find(".input");                                                         // else, find the div:s with class="input" and cache them with a key equal to the current step id
        cache[id].detach().appendTo('#summaryContainer').show().find(":input").removeAttr("disabled");  // detach the cached inputs and append them to the summary container, also show and enable them
    });
}

So you can either invoke that code when you post your return or do something like it to enable the inputs on the page.

Edit:

From your comments I would suggest using this to solve your problem so you consistently manipulate the objects on the page with the same utilities and librarys. Plus JQuery is just dang convenient.

$(':input.ui-wizard-content').each(function() {$( this ).removeAttr("disabled");});

:input gets all inputs on the page. .ui-wizard-content tells JQuery to only look for inputs with that class and then .each loops through each object running the lambda function (function() {$( this ).removeAttr("disabled");})on the objects found.

Edit 2:

It's hard to tell what $row[] is in your code. It's obviously a container but the code you gave is a bit out of context so I can't be sure what it is.

That being said if I only had the DOM (Java scripts representation of whats on the page) to work with and I know about the function you posted earlier I would do something like this.

Replace

if(data.isLastStep){                                                                                    // if this is the last step...then
    $("#summaryContainer").empty();                                                                     // empty the container holding the 
    $.each(data.activatedSteps, function(i, id){                                                        // for each of the activated steps...do
        if(id === "summary") return;                                                                    // if it is the summary page then just return
        cache[id] = $("#" + id).find(".input");                                                         // else, find the div:s with class="input" and cache them with a key equal to the current step id
        cache[id].detach().appendTo('#summaryContainer').show().find(":input").removeAttr("disabled");  // detach the cached inputs and append them to the summary container, also show and enable them
    });
}

with

if(data.isLastStep){ 
    $("#summaryContainer").empty();
    $('.input :input').detach().appendTo('#summaryContainer').show().find(":input").removeAttr("disabled");
}

Which is mostly borrowed from what the previous coder was doing. He assumed that everything in the form would be in data.activatedSteps because of the workflow he created but that's no longer the case. So you either need to see where he adds data to data.activatedSteps and on reload put the saved stuff in there the same way. Or just use my lines which ignore all that data stuff and just grabs every <input> inside an object with the class input and adds it to the container.

Note: JQuery's $(TEXT) uses the same object defining syntax as CSS with some special ease of use add ins like ':input'. There is a great reference to what I'm talking about here for CSS References and for the JQuery special selector syntax's check here. This is how I know how to write things that grab everything you're interested in with a very compact statement.

like image 126
Dan Avatar answered Sep 25 '22 18:09

Dan