Changeset 96


Ignore:
Timestamp:
Jan 19, 2010, 4:18:33 PM (13 years ago)
Author:
duh
Message:
  • as of jQuery 1.4.0.1 the submitToRemote behaviour has changed which broke the taglibrary functionality. The taglibrary now checks for the jQuery plugin version in order to fix the issue and stay backward compatible
Location:
trunk/grails-app
Files:
3 deleted
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/taglib/dbnp/studycapturing/WizardTagLib.groovy

    r92 r96  
    11package dbnp.studycapturing
     2
    23import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib
    34
     
    56 * Wizard tag library
    67 *
    7  * @author  Jeroen Wesbeek
    8  * @since   20100113
     8 * @author Jeroen Wesbeek
     9 * @since 20100113
    910 * @package wizard
    1011 *
     
    1516 */
    1617class WizardTagLib extends JavascriptTagLib {
    17   // define the tag namespace (e.g.: <wizard:action ... />
    18   static namespace = "wizard"
     18        // define the tag namespace (e.g.: <wizard:action ... />
     19        static namespace = "wizard"
    1920
    20   // define the AJAX provider to use
    21   static ajaxProvider = "jquery"
     21        // define the AJAX provider to use
     22        static ajaxProvider = "jquery"
    2223
    23   // define default text field width
    24   static defaultTextFieldSize = 25;
    25  
    26   /**
    27    * ajaxButton tag, this is a modified version of the default
    28    * grails submitToRemote tag to work with grails webflows.
    29    * Usage is identical to submitToRemote with the only exception
    30    * that a 'name' form element attribute is required. E.g.
    31    * <wizard:ajaxButton name="myAction" value="myButton ... />
    32    *
    33    * @see   http://blog.osx.eu/2010/01/18/ajaxifying-a-grails-webflow/
    34    * @see   http://www.grails.org/WebFlow
    35    * @see   http://www.grails.org/Tag+-+submitToRemote
    36    * @todo  perhaps some methods should be moved to a more generic
    37    *        'webflow' taglib
    38    * @param Map     attributes
    39    * @param Closure body
    40    */
    41   def ajaxButton = { attrs, body ->
    42     // fetch the element name from the attributes
    43     def elementName = attrs['name'].replaceAll(/ /,"_")
    44    
    45     // generate a normal submitToRemote button
    46     def button = submitToRemote(attrs,body)
     24        // define default text field width
     25        static defaultTextFieldSize = 25;
    4726
    48     /**
    49      * as of now (grails 1.2.0 and jQuery 1.3.2.4) the grails webflow does
    50      * not properly work with AJAX as the submitToRemote button does not
    51      * handle and submit the form properly. In order to support webflows
    52      * this method modifies two parts of a 'normal' submitToRemote button:
    53      *
    54      * 1) replace 'this' with 'this.form' as the 'this' selector in a button
    55      *    action refers to the button and / or the action upon that button.
    56      *    However, it should point to the form the button is part of as the
    57      *    the button should submit the form data.
    58      * 2) prepend the button name to the serialized data. The default behaviour
    59      *    of submitToRemote is to remove the element name altogether, while
    60      *    the grails webflow expects a parameter _eventId_BUTTONNAME to execute
    61      *    the appropriate webflow action. Hence, we are going to prepend the
    62      *    serialized formdata with an _eventId_BUTTONNAME parameter.
    63      */
    64     button = button.replaceFirst(/data\:jQuery\(this\)\.serialize\(\)/, "data:\'_eventId_${elementName}=1&\'+jQuery(this.form).serialize()")
     27        /**
     28         * ajaxButton tag, this is a modified version of the default
     29         * grails submitToRemote tag to work with grails webflows.
     30         * Usage is identical to submitToRemote with the only exception
     31         * that a 'name' form element attribute is required. E.g.
     32         * <wizard:ajaxButton name="myAction" value="myButton ... />
     33         *
     34         * @see http://blog.osx.eu/2010/01/18/ajaxifying-a-grails-webflow/
     35         * @see http://www.grails.org/WebFlow
     36         * @see http://www.grails.org/Tag+-+submitToRemote
     37         * @todo perhaps some methods should be moved to a more generic
     38         *        'webflow' taglib
     39         * @param Map attributes
     40         * @param Closure body
     41         */
     42        def ajaxButton = {attrs, body ->
     43                // get the jQuery version
     44                def jQueryVersion = grailsApplication.getMetadata()['plugins.jquery']
    6545
    66     // render button
    67     out << button
    68   }
     46                // fetch the element name from the attributes
     47                def elementName = attrs['name'].replaceAll(/ /, "_")
    6948
    70   /**
    71    * wizard navigation buttons render wrapper, in order to be able to add
    72    * functionality in the future
    73    */
    74   def previousNext = { attrs ->
    75     // define AJAX provider
    76     setProvider([library:ajaxProvider])
     49                // generate a normal submitToRemote button
     50                def button = submitToRemote(attrs, body)
    7751
    78     // render navigation buttons
    79     out << render(template:"/wizard/common/buttons")
    80   }
     52                /**
     53                 * as of now (grails 1.2.0 and jQuery 1.3.2.4) the grails webflow does
     54                 * not properly work with AJAX as the submitToRemote button does not
     55                 * handle and submit the form properly. In order to support webflows
     56                 * this method modifies two parts of a 'normal' submitToRemote button:
     57                 *
     58                 * 1) replace 'this' with 'this.form' as the 'this' selector in a button
     59                 *    action refers to the button and / or the action upon that button.
     60                 *    However, it should point to the form the button is part of as the
     61                 *    the button should submit the form data.
     62                 * 2) prepend the button name to the serialized data. The default behaviour
     63                 *    of submitToRemote is to remove the element name altogether, while
     64                 *    the grails webflow expects a parameter _eventId_BUTTONNAME to execute
     65                 *    the appropriate webflow action. Hence, we are going to prepend the
     66                 *    serialized formdata with an _eventId_BUTTONNAME parameter.
     67                 */
     68                if (jQueryVersion =~ /^1.([1|2|3]).(.*)/) {
     69                        // fix for older jQuery plugin versions
     70                        button = button.replaceFirst(/data\:jQuery\(this\)\.serialize\(\)/, "data:\'_eventId_${elementName}=1&\'+jQuery(this.form).serialize()")
     71                } else {
     72                        // as of jQuery plugin version 1.4.0.1 submitToRemote has been modified and the
     73                        // this.form part has been fixed. Consequently, our wrapper has changed as well...
     74                        button = button.replaceFirst(/data\:jQuery/, "data:\'_eventId_${elementName}=1&\'+jQuery")
     75                }
    8176
    82   /**
    83    * render the content of a particular wizard page
    84    * @param Map     attrs
    85    * @param Closure body
    86    */
    87   def pageContent = { attrs, body ->
    88     // define AJAX provider
    89     setProvider([library:ajaxProvider])
     77                // render button
     78                out << button
     79        }
    9080
    91     // render new body content
    92     out << render(template:"/wizard/common/tabs")
    93     out << '<div class="content">'
    94     out << body()
    95     out << '</div>'
    96     out << render(template:"/wizard/common/navigation")
    97   }
     81        /**
     82         * wizard navigation buttons render wrapper, in order to be able to add
     83         * functionality in the future
     84         */
     85        def previousNext = {attrs ->
     86                // define AJAX provider
     87                setProvider([library: ajaxProvider])
    9888
    99   def textFieldElement = { attrs, body ->
    100     // set default size, or scale to max length if it is less than the default size
    101     if (!attrs.get("size")) {
    102       if (attrs.get("maxlength")) {
    103         attrs.size = ((attrs.get("maxlength") as int) > defaultTextFieldSize) ? defaultTextFieldSize : attrs.get("maxlength")
    104       } else {
    105         attrs.size = defaultTextFieldSize
    106       }
    107     }
     89                // render navigation buttons
     90                out << render(template: "/wizard/common/buttons")
     91        }
    10892
    109     // render a text element
    110     out << '<div class="element">'
    111     out << ' <div class="description">'
    112     out << body()
    113     out << ' </div>'
    114     out << ' <div class="input">'
    115     out << textField(attrs)
    116     out << ' </div>'
     93        /**
     94         * render the content of a particular wizard page
     95         * @param Map attrs
     96         * @param Closure body
     97         */
     98        def pageContent = {attrs, body ->
     99                // define AJAX provider
     100                setProvider([library: ajaxProvider])
    117101
    118     // add help icon?
    119     if (attrs.get('help')) {
    120       out << ' <div class="help">'
    121       out << '  <div class="icon"><img src="../images/icons/famfamfam/help.png"></div>'
    122       out << '  <div class="content">'
    123       out << '   <div class="text">'
    124       out << '    '+attrs.get('help')
    125       out << '   </div>'
    126       out << '  </div>'
    127       out << ' </div>'
    128     }
    129    
    130     out << '</div>'
    131   }
     102                // render new body content
     103                out << render(template: "/wizard/common/tabs")
     104                out << '<div class="content">'
     105                out << body()
     106                out << '</div>'
     107                out << render(template: "/wizard/common/navigation")
     108        }
     109
     110        def textFieldElement = {attrs, body ->
     111                // set default size, or scale to max length if it is less than the default size
     112                if (!attrs.get("size")) {
     113                        if (attrs.get("maxlength")) {
     114                                attrs.size = ((attrs.get("maxlength") as int) > defaultTextFieldSize) ? defaultTextFieldSize : attrs.get("maxlength")
     115                        } else {
     116                                attrs.size = defaultTextFieldSize
     117                        }
     118                }
     119
     120                // render a text element
     121                out << '<div class="element">'
     122                out << ' <div class="description">'
     123                out << body()
     124                out << ' </div>'
     125                out << ' <div class="input">'
     126                out << textField(attrs)
     127                out << ' </div>'
     128
     129                // add help icon?
     130                if (attrs.get('help')) {
     131                        out << ' <div class="help">'
     132                        out << '  <div class="icon"><img src="../images/icons/famfamfam/help.png"></div>'
     133                        out << '  <div class="content">'
     134                        out << '   <div class="text">'
     135                        out << '    ' + attrs.get('help')
     136                        out << '   </div>'
     137                        out << '  </div>'
     138                        out << ' </div>'
     139                }
     140
     141                out << '</div>'
     142        }
    132143}
  • trunk/grails-app/views/layouts/main.gsp

    r54 r96  
    22"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    33<html xmlns="http://www.w3.org/1999/xhtml" lang="en-EN" xml:lang="en-EN">
    4  <head>
    5   <title><g:layoutTitle default="Grails" /></title>
    6   <link rel="stylesheet" href="${resource(dir:'css',file:session.style+'.css')}" />
    7   <link rel="stylesheet" href="${resource(dir:'css',file:'login_panel.css')}" />
    8   <link rel="shortcut icon" href="${resource(dir:'images',file:'favicon.ico')}" type="image/x-icon" />
    9   <my:jquery/>
    10   <!-- layouthead //-->
    11   <g:layoutHead />
    12   <!-- /layouthead //-->
    13   <script type="text/javascript" src="${resource(dir:'js', file:'login_panel.js')}"></script>
    14   <script type="text/javascript" src="${resource(dir:'js', file:'topnav.js')}"></script>
    15  </head>
    16  <body>
    17   <g:render template="/common/login_panel" />
    18   <div class="container">
    19    <div id="header">
    20     <g:render template="/common/topnav" />
    21    </div>
    22    <div id="content"><g:layoutBody /></div>
    23    <div id="footer">
    24      Copyright © 2008 - <g:formatDate format="yyyy" date="${new Date()}"/> NMC & NuGO. All rights reserved.
    25      ( style: <%=session.style%>,
    26      <a href="?showSource=true">show page source</a>)</div>
    27   </div>
    28  </body>
     4<head>
     5        <title><g:layoutTitle default="Grails"/></title>
     6        <link rel="stylesheet" href="${resource(dir: 'css', file: session.style + '.css')}"/>
     7        <link rel="stylesheet" href="${resource(dir: 'css', file: 'login_panel.css')}"/>
     8        <link rel="shortcut icon" href="${resource(dir: 'images', file: 'favicon.ico')}" type="image/x-icon"/>
     9        <g:javascript library="jquery"/>
     10        <script src="${createLinkTo(dir: 'js', file: 'jquery-ui-1.7.2.custom.min.js')}" type="text/javascript"></script>
     11        <link rel="stylesheet" href="${createLinkTo(dir: 'css/jquery-ui', file: 'jquery-ui-1.7.2.custom.css')}"/>
     12        <!-- layouthead //-->
     13        <g:layoutHead/>
     14        <!-- /layouthead //-->
     15        <script type="text/javascript" src="${resource(dir: 'js', file: 'login_panel.js')}"></script>
     16        <script type="text/javascript" src="${resource(dir: 'js', file: 'topnav.js')}"></script>
     17</head>
     18<body>
     19<g:render template="/common/login_panel"/>
     20<div class="container">
     21        <div id="header">
     22                <g:render template="/common/topnav"/>
     23        </div>
     24        <div id="content"><g:layoutBody/></div>
     25        <div id="footer">
     26                Copyright © 2008 - <g:formatDate format="yyyy" date="${new Date()}"/> NMC & NuGO. All rights reserved.
     27                ( style: <%=session.style%>,
     28                <a href="?showSource=true">show page source</a>)</div>
     29</div>
     30</body>
    2931</html>
Note: See TracChangeset for help on using the changeset viewer.