SitePoint Sponsor

User Tag List

Results 1 to 7 of 7
  1. #1
    SitePoint Member
    Join Date
    May 2004
    Location
    Germany
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    WACT Question: How do I implement a "multi page form" with one FormPage controller?

    Hi,

    right now I'm getting desperate on figuring out a way to handle a "logical" form, that consists of several "physical" form pages like "insert data -> click next button -> insert data-> click save button", by using a single FormPage controller and a single FormView. (btw I' using the integration build of july. I know things changed since then, but I'm not yet familiar with the new stuff)

    The basic idea is to write a template that conatins the form elements of all form steps, and hinder them of being displayed alltogether by wrapping them into some <core:block> tags. The first block of form elements is displayed by default, the second block will appear as soon as the user hits the "next" button and so on.

    When it comes to form validation I run into problems, as there can be registered only one Validator object within a PageController. So this Validator must contain all the validation rules for all form elements, because the Validator object is not aware of the form elements currently being displayed. That of course will result in an invalid form, since some defined rules won't find the corresponding field to validate.

    My Question is, how can I add a "now I'm at step X of the form" functionality to the PageController, which allows me to validate and process only the latest posted form elements.

    Some people may ask, why not display all form elements on a single page? The answer is, some elements are rendered dynamically on certain conditions of the previous form.

    I hope I could point out my problem.

    Some ideas of the masteminds around here?

    Any help would make me a happy man,
    Tito

  2. #2
    Non-Member
    Join Date
    Oct 2004
    Location
    downtown
    Posts
    145
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    My Question is, how can I add a "now I'm at step X of the form" functionality to the PageController, which allows me to validate and process only the latest posted form elements.
    Simple put I'd consider a SWITCH to be about the best option for you. Have a variable in each FORM block to determine the case to use within the SWITCH

    But I think the best option is to seperate the processes you have, how I'm not sure as I've not used WACT

    Hope this helps anyways.

  3. #3
    SitePoint Member
    Join Date
    May 2004
    Location
    Germany
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh if it would be that easy!

    the WACT FormPage controller is a bit more tricky.

    but thanks anyway,
    Tito

  4. #4
    SitePoint Member
    Join Date
    Jul 2004
    Location
    Budapest, Hungary
    Posts
    24
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    workin on the same stuff

    I have to implement a multi-page form actually too. By searching the web, I've found the following stuff, maybe it's useful for you.

    http://sourceforge.net/tracker/index...72&atid=575987
    http://sourceforge.net/mailarchive/m...msg_id=7037455

    Tell me if you found a better solution!

    Regards,
    Norbert

  5. #5
    SitePoint Member
    Join Date
    May 2004
    Location
    Germany
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi norbert,

    I actually figured out some way recently. My approach is slightly different from the one of Russel Smith (btw thanks for the link), not only because I use the other controller structure provided in WACT.
    The basic idea behind this, is to write a template like the following


    form.tpl
    HTML Code:
    <form id="AddProduct" method="POST" enctype="multipart/form-data" runat="server">
    
    
        <core:block id="Step1" show>
    
            <input type="hidden" name="PageIndex" id="FormPage1" value="1" runat="client">
    
            <!-- Insert form fields here -->
    
            <input type="submit" name="next" id="next1" value="Next Page">
    
        </core:block>
    
    
        <core:block id="Step2" hide>
        <!--Start Step2 of Form processing-->
    
            <input type="hidden" name="PageIndex" id="PageIndex2" value="2" runat="client">
    
            <!-- Insert form fields here -->
    
            <input type="submit" name="next" id="next2" value="Next Page">
    
        <core:include file="ProductFields2.tpl">
    
        <!--End Step2 of Form processing-->
        </core:block>
    
        <core:block id="Step3" hide>
    
        <core:include file="details.tpl">
            <input type="hidden" name="PageIndex" id="PageIndex3" value="3" runat="client">
    
    
            <!-- Insert form fields here -->
    
    
    
            <input type="submit" name="back" id="backbutton" value="korrigieren">
            &nbsp;
            <input type="submit" name="save" id="savebutton" value="Speichern">
    
        </core:block>
    
    </form>

    As you can see, each block contains a hidden field that stores the form page index. That index identifies the block being displyed later.

    Now for most crucial thing, the page controller. All it does is to tell the Validator, which page to validate, and after that to tell the View, which page will be displayed (dependin on the form is valid or not). Its not a perfect solution, but it fits my goals quite well. Further more, I have to admit that this controller only works in one direction like page1->page2->. To add a "previous page" logic, I recommend an additional class attribute, that stores the "previous button" name. Then you can rewrite the iteratePageIndex() method to check, whether that button has been hit or not (Just a thought out of the blue).

    I extended the FormPage controller class of WACT to add the multi-page logic, so only the overwritten and added stuff is shown here:


    multipage.inc.php
    PHP Code:
    /**Provides multi page logic for form pages
    *   Right now it works only in one direction (Page1 -> Page2 -> Page3),
    *   but to add a bidirectional logic shouldn't be a big thing
    */
    class MultiPageFormPage extends FormPage {


        
    /**Switches the multi-page form logic on an off (default off)
        *   @var    bool
        *   @access protected
        */
        
    var $isMultiPageForm false;
        
        
    /**Stores the page index value
        *   @var    integer
        *   @access private
        */
        
    var $PageIndex null;
       

        
    /**Name of the hidden field within the template file that stores the page index 
        * This can be overwritten to fit your desires.
        *   @var    string
        *   @access protected
        */
        
    var $PageIndexName 'PageIndex';


        function 
    MultiPageFormPage(){
            
    parent::FormPage();
        }

        
        
    /**Sets the value of the form currently being displayed
        *This method is needed for multipaged forms
        *   @access protected
        */
        
    function setPageIndex(){
            if(
    $this->isMultiPageForm){
                
    $this->PageIndex $this->Model->get($this->PageIndexName);
            }
        }


        
    /**Iterates the form page index currently being displayed in order
        *    to get the next page
        *This method is needed for multipaged forms
        *   @access protected
        */
        
    function iternatePageIndex(){
            if(
    $this->isMultiPageForm){
                
    $this->PageIndex $this->Model->get($this->PageIndexName) +1;

             }
        }


        
    /**Adds the page index as parameter to the ValidatorHandle
        *   @access private
        */
        
    function setValidatorParam(){

            if(
    is_string($this->ValidatorHandle) && $this->isMultiPageForm) {
                
    $this->ValidatorHandle = array($this->ValidatorHandle,   
                                                       
    $this->Model->get($this->PageIndexName));
            }
        }


        
    /**
        * Executes the validation of the form.
        * overrides the FormPage::validate() method
        * @return void
        * @access private
        */
        
    function validate() {
            if (!
    $this->Validated && isset($this->ValidatorHandle)) {

                
    //attach the page index parameter to the registered 
                //validator before resolving it
                
    $this->setValidatorParam();
                
    ResolveHandle($this->ValidatorHandle);
                
    $this->ValidatorHandle->validate($this->Model);
                
    $this->Valid $this->ValidatorHandle->IsValid();
                
    $this->Validated TRUE;
            }
        }

         
    /**This method overrides the standard displayView() method 
         *Adds logic to support multipaged forms
         *  overrides FormPage::displayView()
         * @return void
         * @access protected
         */
        
    function displayView() {
            
    // Transfer data values from the model into the form.
            
    $this->ViewHandle->setModel($this->Model);

            if (
    method_exists($this->ViewHandle'setErrors')) {

                
    // force validation if necessary
                
    if ($this->MethodControllerHandle->IsFormPosted()) {
                    
    $this->validate();


                    
    //if form is valid, iternate page index, and transfer the 
                    //form page index to the View
                    
    if($this->Valid && $this->isMultiPageForm){
                        
    $this->iternatePageIndex();
                        
    $this->ViewHandle->setPageIndex($this->PageIndex);
                    }
                    else {      
                        
    //if form is not valid, transfer the current index to the view in 
                        //order to display the same View again

                    
    $this->setPageIndex();
                        
    $this->ViewHandle->setPageIndex($this->PageIndex);
                    }
                }


                
    // Transfer the list of errors into the form.
                
    if ($this->Validated && !$this->Valid &&
                                    isset(
    $this->ValidatorHandle)) {

                    
    //attach the page index parameter to 
                    //the registered validator before resolving it
                    
    $this->setValidatorParam();
                    
    ResolveHandle($this->ValidatorHandle);
                    
    $this->ViewHandle->setErrors($this->ValidatorHandle);
                }

                
    // Establish client side validation.
                
    if (isset($this->ValidatorHandle) && 
                     
    $this->ViewHandle->canClientValidate()) {

                    
    //attach the page index parameter to the registered 
                    //validator before resolving it
                    
    $this->setValidatorParam();
                    
    ResolveHandle($this->ValidatorHandle);
                   
    $this->ViewHandle->setClientValidation($this->ValidatorHandle);
                }

                if (!
    $this->MethodControllerHandle->IsFormPosted()) {
                    
    $this->ViewHandle->onInitial();
                } else {
                    
    $this->ViewHandle->onSubmitted();
                }
            }

            
    $this->ViewHandle->prepare();
            
    $this->ViewHandle->display();
        }


    The setValidatorParam() method is some kind of a makeshift. There is no real need to add a parameter to an existing validator. Its also possible to register a new one each page.


    Adding the needed logic to the view is quite simple:

    mutlipageformview.inc.php
    PHP Code:
    /**Provides multi page logic for form pages
    *
    */
    class MultiPageFormView extends FormView {

        
    /**Stores the index of the form page, that is to be displayed
        *   @var integer
        *   @access private
        */
        
    var $PageIndex null;


        
    /**Constructor
        *
        */
        
    function MultiPageFormView($TemplateFile$PreservedField){
            
    parent::FormView($TemplateFile$PreservedField);
        }


        
    /**set the current page index of the View.
        *   this method is only used by the responsible page controller
        *   @access public
        *   @param integer
        */
        
    function setPageIndex($PageIndex){
            
    $this->PageIndex $PageIndex;
        }



    That's all. Let's have a look at a small example:


    The page contoller just extends the MultiPageFormPage controller:


    PHP Code:
    class CustomPage extend MultiPageFormPage {

        function 
    CustomPage($ResourceType){
            
    $this->isMultiPageForm true;

            
    //add some actions

            //register the validator (the parameter will be set by the 
            //setValidatorParam() method)
            
    $this->registerValidator(custom.validation.php|CustomValidator);

            
    //register the View with the form.tpl shown above
            
    $this->registerView(array(custom.view.php|CustomView'form.tpl'));


            
    parent::MultiPageFormPage();
        }



    custom.validation.php
    PHP Code:
    class CustomValidator extends Validator {

        
    /**
        *
        */
        
    function CustomValidator($PageIndex){
            switch(
    $PageIndex){
                case 
    2:
                    
    //validate corresponding fields
                    
    break;
                case 
    3:
                    
    //validate corresponding fields
                    
    break;
                default:
                    
    //validate corresponding fields
            
    }
        }

    custom.view.php
    PHP Code:
    class CustomView extends MultiPageFormView {

        function 
    CustomView ($TemplateFile$PreservedFields) {
            
    parent::MultiPageFormView($TemplateFile$PreservedFields);
        }

        function 
    prepare(){

            switch(
    $this->PageIndex){
                case 
    2:
                    
    $HideBlock = &$this->Form->getChild('Step1);
                    $HideBlock->hide();
                    $ShowBlock = &$this->Form->getChild('
    Step2);
                    
    $ShowBlock->show();
                    
    //and so on
                    
    break;
                case 
    3:
                    
    //do hide and show.....

                    
    break;
                default:

            }
        }


    I'm sure this example won't work out of the box. I didn't spend much time to review what I've written, but I hope that the multi-page controller and -view will work without any modification.

    Of course everyone is welcome to comment on this, but please don't crucify me. I'm very well aware of my poor coding skills.


    So far,
    Alex

  6. #6
    SitePoint Member
    Join Date
    Jul 2004
    Location
    Budapest, Hungary
    Posts
    24
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for your reply, it gave me lots of ideas. I'll think about this on the way home this evening

    Regards,
    Norbert

  7. #7
    SitePoint Member
    Join Date
    May 2004
    Location
    Germany
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ok,

    as soon as some of your ideas result in some lines of code, let me know.

    regards,
    Tito


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •