Changeset 101


Ignore:
Timestamp:
Jan 20, 2010, 5:01:26 PM (13 years ago)
Author:
duh
Message:
  • refactored the wizard initial page to dynamically load pageOne, instead of rendering pageOne within the template itself. This was done because otherwise both the mainPage and pageOne had to contain duplicate logic. The ajaxified webflow works better this way.
  • added an <wizard:ajaxFlowRedirect...> tag which renders javascript code executing a jQuery ajax call (it actually wraps around <wizard:button...> tag but lifts the ajax call out of the button and wraps javascript tags around it)
  • improved the help / tooltip workings
  • extended the <wizard:button...> with an afterSuccess argument which executes some javascript after success. This is different from the default submitToRemote 'after' behaviour which is always executed in parallel with the ajax success call (hence, you javascript cannot access ajax result data while the afterSuccess method can)
Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy

    r88 r101  
    11package dbnp.studycapturing
     2
    23import dbnp.studycapturing.*
    34import grails.converters.*
     
    910 * through the study capturing wizard.
    1011 *
    11  * @author  Jeroen Wesbeek
    12  * @since   20100107
     12 * @author Jeroen Wesbeek
     13 * @since 20100107
    1314 * @package studycapturing
    1415 *
     
    1920 */
    2021class WizardController {
    21     /**
    22     * index method, redirect to the webflow
    23     * @void
    24     */
    25     def index = {
    26       /**
    27       * Do you believe it in your head?
    28       * I can go with the flow
    29       * Don't say it doesn't matter (with the flow) matter anymore
    30       * I can go with the flow (I can go)
    31       * Do you believe it in your head?
    32       */
    33       redirect(action:'pages')
    34     }
     22        /**
     23        * index method, redirect to the webflow
     24        * @void
     25        */
     26        def index = {
     27                /**
     28                * Do you believe it in your head?
     29                * I can go with the flow
     30                * Don't say it doesn't matter (with the flow) matter anymore
     31                * I can go with the flow (I can go)
     32                * Do you believe it in your head?
     33                */
     34                redirect(action: 'pages')
     35        }
    3536
    36     /**
    37      * WebFlow definition
    38      * @see http://grails.org/WebFlow
    39      * @void
    40      */
    41     def pagesFlow = {
    42       // start the flow
    43       onStart {
    44         println "wizard started"
    45        
    46         // define flow variables
    47         flow.page = 0
    48         flow.pages = [
    49                 [title:'Een'],
    50                 [title:'Twoooo'],
    51                 [title:'Trois']
    52         ]
    53        
    54       }
    55       onEnd {
    56             println "wizard ended"
    57           }
     37        /**
     38         * WebFlow definition
     39         * @see http://grails.org/WebFlow
     40         * @void
     41         */
     42        def pagesFlow = {
     43                // start the flow
     44                onStart {
     45                        println "wizard started"
    5846
    59           // render the main wizard page
    60           mainPage {
    61             onRender {
    62                   println "render main page"
    63           flow.page = 1
    64             }
    65             render(view:"/wizard/index")
    66         on("next") {
    67           println "next page!"
    68         }.to "pageTwo"
    69       }
     47                        // define flow variables
     48                        flow.page = 0
     49                        flow.pages = [
     50                                [title: 'Een'],
     51                                [title: 'Twoooo'],
     52                                [title: 'Trois']
     53                        ]
    7054
    71       pageOne {
    72             onRender {
    73                   println "render page one"
    74           flow.page = 1
    75             }
    76             render(view:"_one")
    77         on("next") {
    78           println "next page!"
    79         }.to "pageTwo"
    80       }
     55                }
    8156
    82           // render page two
    83           pageTwo {
    84             onRender {
    85                   println "render page two"
    86           flow.page = 2
    87             }
    88         render(view:"_two")
    89         on("next") {
    90           println "next page!"
    91         }.to "pageThree"
    92         on("previous") {
    93           println "previous page!"
    94         }.to "pageOne"
    95           }
     57                // render the main wizard page
     58                mainPage {
     59                        render(view: "/wizard/index")
     60                        onRender {
     61                                flow.page = 1
     62                        }
     63                        on("next").to "pageOne"
     64                }
    9665
    97           // render page three
    98           pageThree {
    99             onRender {
    100                   println "render page three"
    101           flow.page = 3
    102             }
    103         render(view:"_three")
    104         on("previous") {
    105           println "previous page!"
    106         }.to "pageTwo"
    107           }
    108     }
     66                pageOne {
     67                        render(view: "_one")
     68                        onRender {
     69                                println "render page one"
     70                                flow.page = 1
     71                        }
     72                        on("next") {
     73                                // create a study instance
     74                                /*
     75                                println params
     76                                def study = new Study(params)
     77
     78                               
     79                                if (study.validate()) {
     80                                        println "study validates"
     81                                } else {
     82                                        println "errorrrs"
     83                                        error()
     84                                }
     85                                */
     86                        }.to "pageTwo"
     87                }
     88
     89                // render page two
     90                pageTwo {
     91                        render(view: "_two")
     92                        onRender {
     93                                println "render page two"
     94                                flow.page = 2
     95                        }
     96                        on("next") {
     97                                println "next page!"
     98                        }.to "pageThree"
     99                        on("previous") {
     100                                println "previous page!"
     101                        }.to "pageOne"
     102                }
     103
     104                // render page three
     105                pageThree {
     106                        render(view: "_three")
     107                        onRender {
     108                                println "render page three"
     109                                flow.page = 3
     110                        }
     111                        on("previous") {
     112                                println "previous page!"
     113                        }.to "pageTwo"
     114                }
     115        }
    109116}
  • trunk/grails-app/domain/dbnp/studycapturing/Study.groovy

    r100 r101  
    2121
    2222        static constraints = {
    23                 owner(nullable:true,blank:true)
    24                 template(nullable:true,blank:true)
     23                owner(nullable: true, blank: true)
     24                title(nullable: false, blank: false)
     25                template(nullable: true, blank: true)
    2526        }
    2627
  • trunk/grails-app/taglib/dbnp/studycapturing/WizardTagLib.groovy

    r99 r101  
    3232         * <wizard:ajaxButton name="myAction" value="myButton ... />
    3333         *
     34         * you can also provide a javascript function to execute after
     35         * success. This behaviour differs from the default 'after'
     36         * action which always fires after a button press...
     37         *
    3438         * @see http://blog.osx.eu/2010/01/18/ajaxifying-a-grails-webflow/
    3539         * @see http://www.grails.org/WebFlow
     
    4044         * @param Closure body
    4145         */
    42         def ajaxButton = {attrs, body ->
     46        def ajaxButton = { attrs, body ->
    4347                // get the jQuery version
    4448                def jQueryVersion = grailsApplication.getMetadata()['plugins.jquery']
     
    4650                // fetch the element name from the attributes
    4751                def elementName = attrs['name'].replaceAll(/ /, "_")
     52
     53                // javascript function to call after success
     54                def afterSuccess = attrs['afterSuccess']
    4855
    4956                // generate a normal submitToRemote button
     
    7481                        button = button.replaceFirst(/data\:jQuery/, "data:\'_eventId_${elementName}=1&\'+jQuery")
    7582                }
    76 
     83 
     84                // add an after success function call?
     85                // usefull for performing actions on success data (hence on refreshed
     86                // wizard pages, such as attaching tooltips)
     87                if (afterSuccess) {
     88                        button = button.replaceFirst(/\.html\(data\)\;/, '.html(data);' + afterSuccess + ';')
     89                }
     90
     91                // replace double semi colons
     92                button = button.replaceAll(/;{2,}/, '!!!')
     93               
    7794                // render button
    7895                out << button
     96        }
     97
     98        /**
     99         * generate ajax webflow redirect javascript
     100         *
     101         * As we have an Ajaxified webflow, the initial wizard page
     102         * cannot contain a wizard form, as upon a failing submit
     103         * (e.g. the form data does not validate) the form should be
     104         * shown again. However, the Grails webflow then renders the
     105         * complete initial wizard page into the success div. As this
     106         * ruins the page layout (a page within a page) we want the
     107         * initial page to redirect to the first wizard form to enter
     108         * the webflow correctly. We do this by emulating an ajax post
     109         * call which updates the wizard content with the first wizard
     110         * form.
     111         *
     112         * Usage: <wizard:ajaxFlowRedirect form="form#wizardForm" name="next" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" />
     113         * form = the form identifier
     114         * name = the action to execute in the webflow
     115         * update = the divs to update upon success or error
     116         *
     117         * Example initial webflow action to work with this javascript:
     118         * ...
     119         * mainPage {
     120         *      render(view: "/wizard/index")
     121         *      onRender {
     122         *              flow.page = 1
     123         *      }
     124         *      on("next").to "pageOne"
     125         * }
     126         * ...
     127         *
     128         * @param Map attributes
     129         * @param Closure body
     130         */
     131        def ajaxFlowRedirect = { attrs, body ->
     132                // define AJAX provider
     133                setProvider([library: ajaxProvider])
     134
     135                // generate an ajax button
     136                def button = this.ajaxButton(attrs, body)
     137
     138                // strip the button part to only leave the Ajax call
     139                button = button.replaceFirst(/<[^\"]*\"jQuery.ajax/,'jQuery.ajax')
     140                button = button.replaceFirst(/return false.*/,'')
     141
     142                // change form if a form attribute is present
     143                if (attrs.get('form')) {
     144                        button = button.replaceFirst(/this\.form/,
     145                                "\\\$('" + attrs.get('form') + "')"
     146                        )
     147                }
     148
     149                // generate javascript
     150                out << '<script language="JavaScript">'
     151                out << '$(document).ready(function() {'
     152                out << button
     153                out << '});'
     154                out << '</script>'
    79155        }
    80156
     
    124200                out << '<div class="element">'
    125201                out << ' <div class="description">'
    126                 out << body()
     202                out << attrs.get('description')
    127203                out << ' </div>'
    128204                out << ' <div class="input">'
     
    131207
    132208                // add help icon?
    133                 if (attrs.get('help')) {
     209                if (body()) {
    134210                        out << ' <div class="help">'
    135211                        out << '  <div class="icon"></div>'
    136212                        out << '  <div class="content">'
    137                         out << '    ' + attrs.get('help')
     213                        out << '    ' + body()
    138214                        out << '  </div>'
    139215                        out << ' </div>'
  • trunk/grails-app/views/wizard/common/_navigation.gsp

    r88 r101  
    1515%>
    1616    <div class="navigation">
    17       <g:if test="${page>1}"><wizard:ajaxButton name="previous" value="&laquo; prev" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" class="prevnext" /></g:if>
     17      <g:if test="${page>1}"><wizard:ajaxButton name="previous" value="&laquo; prev" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess="attachHelpTooltips()" class="prevnext" /></g:if>
    1818      <g:if test="${page>1 && page<pages.size}"> | </g:if>
    19       <g:if test="${page<pages.size}"><wizard:ajaxButton name="next" value="next &raquo;" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" class="prevnext" /></g:if>
     19      <g:if test="${page<pages.size}"><wizard:ajaxButton name="next" value="next &raquo;" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess="attachHelpTooltips()" class="prevnext" /></g:if>
    2020    </div>
  • trunk/grails-app/views/wizard/common/_wizard.gsp

    r88 r101  
    11<%
    2 /**
    3  * Wizard template with first page rendered
    4  *
    5  * @author Jeroen Wesbeek
    6  * @since  20100113
    7  * @package wizard
    8  * @see    dbnp.studycapturing.WizardTagLib
    9  * @see    dbnp.studycapturing.WizardController
    10  *
    11  * Revision information:
    12  * $Rev$
    13  * $Author$
    14  * $Date$
    15  */
     2        /**
     3        * Wizard template with first page rendered
     4        *
     5         * @author Jeroen Wesbeek
     6         * @since 20100113
     7        * @package wizard
     8         * @see dbnp.studycapturing.WizardTagLib
     9         * @see dbnp.studycapturing.WizardController
     10        *
     11        * Revision information:
     12        * $Rev$
     13        * $Author$
     14        * $Date$
     15        */
    1616%>
    17   <div id="wizard" class="wizard">
    18     <h1>Proof of concept AJAXified Grails Webflow Wizard</h1>
    19     <g:form action="pages" name="_wizard" >
    20     <div id="wizardPage">
    21       <g:render template="pages/one"/>
    22     </div>
    23     <g:render template="common/error"/>
    24     </g:form>
    25   </div>
     17<div id="wizard" class="wizard">
     18        <h1>Proof of concept AJAXified Grails Webflow Wizard</h1>
     19        <g:form action="pages" name="wizardForm" id="wizardForm">
     20                <div id="wizardPage">
     21                        <wizard:ajaxFlowRedirect form="form#wizardForm" name="next" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess="handleHelpTooltips()" />
     22                </div>
     23                <g:render template="common/error"/>
     24        </g:form>
     25</div>
  • trunk/grails-app/views/wizard/pages/_one.gsp

    r92 r101  
    11<%
    22/**
    3  * Wizard page one
     3 * Study page
    44 *
    55 * @author  Jeroen Wesbeek
     
    1616%>
    1717<wizard:pageContent>
    18         <wizard:textFieldElement name="studyTitle" help="The title of the study you are creating">Title</wizard:textFieldElement>
    19         <wizard:textFieldElement name="studyCode" help="A code to reference your study by">Code</wizard:textFieldElement>
    20         <wizard:textFieldElement name="studyResearchQuestion" help="The research question">Research Question</wizard:textFieldElement>
    21         <wizard:textFieldElement name="studyDescription">Description</wizard:textFieldElement>
    22         <wizard:textFieldElement name="studyEcCode">Ethical Committee Code</wizard:textFieldElement>
     18        <wizard:textFieldElement name="studyTitle" description="Title">
     19                The title of the study you are creating
     20        </wizard:textFieldElement>
     21        <wizard:textFieldElement name="studyCode" description="Code">
     22                A code to reference your study by
     23        </wizard:textFieldElement>
     24        <wizard:textFieldElement name="studyResearchQuestion" description="Research Question">
     25                The research question
     26        </wizard:textFieldElement>
     27        <wizard:textFieldElement name="studyDescription" description="Description" />
     28        <wizard:textFieldElement name="studyEcCode" description="Ethical Committee Code" />
    2329</wizard:pageContent>
  • trunk/grails-app/views/wizard/pages/_three.gsp

    r92 r101  
    11<%
    2 /**
    3  * Wizard page three
    4  *
    5  * @author Jeroen Wesbeek
    6  * @since  20100113
    7  * @package wizard
    8  * @see    dbnp.studycapturing.WizardTagLib::previousNext
    9  * @see    dbnp.studycapturing.WizardController
    10  *
    11  * Revision information:
    12  * $Rev$
    13  * $Author$
    14  * $Date$
    15  */
     2        /**
     3        * Wizard page three
     4        *
     5         * @author Jeroen Wesbeek
     6         * @since 20100113
     7        * @package wizard
     8         * @see dbnp.studycapturing.WizardTagLib::previousNext
     9         * @see dbnp.studycapturing.WizardController
     10        *
     11        * Revision information:
     12        * $Rev$
     13        * $Author$
     14        * $Date$
     15        */
    1616%>
    1717<wizard:pageContent>
    18 <wizard:textFieldElement name="myZero" value="12" help="some help text" maxlength="4">more than four?</wizard:textFieldElement>
    19 <wizard:textFieldElement name="myFirstName" value="my value" help="my help">Please fill in you name bla bla bla</wizard:textFieldElement>
    20 <wizard:textFieldElement name="mySecondName" value="1234" size="4">another text field</wizard:textFieldElement>
    21 <wizard:textFieldElement name="myThirdName" value="a lotta description" help="so much for help">
    22   Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur pretium dignissim tellus, id pharetra erat tempus sed. Sed bibendum libero eu lorem pretium nec fermentum ligula faucibus. Morbi gravida interdum ornare. Praesent lectus mi, ullamcorper nec semper nec, vulputate ornare elit. Nam eros metus, egestas a varius eget, facilisis ac purus. Maecenas lectus erat, rutrum id consequat ac, scelerisque ut diam. Donec euismod, tellus facilisis semper elementum, neque lorem volutpat ante, ac consectetur lectus ante sit amet neque. Donec hendrerit, libero quis suscipit iaculis, lacus ligula viverra nibh, eu condimentum diam dui sit amet quam. Praesent turpis orci, laoreet sodales adipiscing eget, ultrices at augue. Nullam sed dolor a velit posuere euismod. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut libero mauris, fermentum id congue sit amet, pharetra in purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam a blandit quam. Cras porta tempus lectus, vel varius lacus vulputate in. Aenean ac nunc lectus, hendrerit tempor elit. Sed ut varius diam.
    23 </wizard:textFieldElement>
     18        <wizard:textFieldElement name="myThirdName" value="a lotta description" description="very fancy help text">
     19                <img src="http://www.grails.org/images/new/grailslogo_topNav.png"/><br/>
     20                This is made with <a href="http://www.grails.org/" target="_new">Grails</a>...
     21        </wizard:textFieldElement>
     22        <wizard:textFieldElement name="myZero" value="12" description="more than four?" maxlength="4">
     23                you cannot enter more than 4 elements here, and also the
     24                width of this field is automatically scaled to the maximum character
     25                size
     26        </wizard:textFieldElement>
     27        <wizard:textFieldElement name="myThirdName" value="a lotta description" description="long help text">
     28                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur pretium dignissim tellus, id pharetra erat tempus sed. Sed bibendum libero eu lorem pretium nec fermentum ligula faucibus. Morbi gravida interdum ornare. Praesent lectus mi, ullamcorper nec semper nec, vulputate ornare elit. Nam eros metus, egestas a varius eget, facilisis ac purus. Maecenas lectus erat, rutrum id consequat ac, scelerisque ut diam.</p>
     29                <p>Donec euismod, tellus facilisis semper elementum, neque lorem volutpat ante, ac consectetur lectus ante sit amet neque. Donec hendrerit, libero quis suscipit iaculis, lacus ligula viverra nibh, eu condimentum diam dui sit amet quam. Praesent turpis orci, laoreet sodales adipiscing eget, ultrices at augue. Nullam sed dolor a velit posuere euismod. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut libero mauris, fermentum id congue sit amet, pharetra in purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
     30                <p>Nullam a blandit quam. Cras porta tempus lectus, vel varius lacus vulputate in. Aenean ac nunc lectus, hendrerit tempor elit. Sed ut varius diam.</p>
     31        </wizard:textFieldElement>
    2432</wizard:pageContent>
  • trunk/web-app/js/wizard.js

    r98 r101  
    1414 */
    1515$(document).ready(function() {
     16    attachHelpTooltips();
     17});
     18
     19function attachHelpTooltips() {
    1620    // attach help action on all wizard help icons
    1721    $('div#wizard').find('div.help').each(function() {
     
    1923            content: 'leftMiddle',
    2024            position: {
    21                corner: {
    22                   tooltip: 'leftMiddle',
    23                   target: 'rightMiddle'
    24                }
     25                corner: {
     26                    tooltip: 'leftMiddle',
     27                    target: 'rightMiddle'
     28                }
    2529            },
    2630            style: {
    27                border: {
    28                   width: 5,
    29                   radius: 10
    30                },
    31                padding: 10,
    32                textAlign: 'center',
    33                tip: true, // Give it a speech bubble tip with automatic corner detection
    34                name: 'blue' // Style it according to the preset 'cream' style
     31                border: {
     32                    width: 5,
     33                    radius: 10
     34                },
     35                padding: 10,
     36                textAlign: 'center',
     37                tip: true, // Give it a speech bubble tip with automatic corner detection
     38                name: 'blue' // Style it according to the preset 'cream' style
    3539            },
    3640            content: $(this).find('div.content').html(),
     
    3943        })
    4044    })
    41 });
     45}
Note: See TracChangeset for help on using the changeset viewer.