• Jump To … +
    beanvalidator-validation.coffee t5-core-dom-jquery.coffee t5-core-dom-prototype.coffee ajax.coffee ajaxformloop.coffee alert.coffee autocomplete.coffee bootstrap.coffee confirm-click.coffee console.coffee datefield.coffee events.coffee exception-display.coffee exception-frame.coffee fields.coffee form-fragment.coffee forms.coffee init.coffee localdate.coffee messages.coffee moment.coffee pageinit.coffee palette.coffee select.coffee time-interval.coffee tree.coffee utils.coffee validation.coffee zone-refresh.coffee zone.coffee
  • forms.coffee

  • ¶

    Copyright 2012, 2013 The Apache Software Foundation

    Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

    http:#www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

  • ¶

    t5/core/forms

    Defines handlers for HTML forms and HTML field elements, specifically to control input validation.

    define ["./events", "./dom", "underscore"],
      (events, dom, _) ->
  • ¶

    Meta-data name that indicates the next submission should skip validation (typically, because the form was submitted by a “cancel” button).

        SKIP_VALIDATION = "t5:skip-validation"
  • ¶

    Data attribute and value added to HTML forms generated by the Tapestry’s Form component from the core library.

        DATA_ATTRIBUTE = "data-generator"
        DATA_ATTRIBUTE_VALUE = "tapestry/core/form"
        TAPESTRY_CORE_FORM_SELECTOR = "form[" + DATA_ATTRIBUTE + "='" + DATA_ATTRIBUTE_VALUE + "']"
    
        clearSubmittingHidden = (form) ->
          hidden = form.findFirst "[name='t:submit']"
  • ¶

    Clear if found

          hidden and hidden.value null
    
          form.meta SKIP_VALIDATION, null
    
          return
    
        setSubmittingHidden = (form, submitter) ->
    
          mode = submitter.attr "data-submit-mode"
          isCancel = mode is "cancel"
          if mode and mode isnt "normal"
            form.meta SKIP_VALIDATION, true
    
          hidden = form.findFirst "[name='t:submit']"
    
          unless hidden
            firstHidden = form.findFirst "input[type=hidden]"
            hidden = dom.create "input", type: "hidden", name: "t:submit"
            firstHidden.insertBefore hidden
  • ¶

    TODO: Research why we need id and name and get rid of one if possible.

          name = if isCancel then "cancel" else submitter.element.name
  • ¶

    Not going to drag in all of json2 just for this one purpose, but even so, I’d like to get rid of this. Prototype includes Object.toJSON(), but jQuery is curiously absent an equivalent.

          hidden.value "[\"#{submitter.element.id}\",\"#{name}\"]"
    
          return
  • ¶

    Passed the element wrapper for a form element, returns a map of all the values for all non-disabled fields (including hidden fields, select, textarea). This is primarily used when assembling an Ajax request for a form submission.

        gatherParameters = (form) ->
          result = {}
    
          fields = form.find "input, select, textarea"
    
          _.each fields, (field) ->
              return if field.attr "disabled"
    
              type = field.element.type
  • ¶

    Ignore types file and submit; file doesn’t make sense for Ajax, and submit is handled by keeping a hidden field active with the data Tapestry needs on the server.

              return if type is "file" or type is "submit"
    
              return if (type is "checkbox" or type is "radio") and field.checked() is false
    
              value = field.value()
    
              return if value is null
    
              name = field.element.name
  • ¶

    Many modern UIs create name-less elements on the fly (e.g., Backbone); these may be mixed in with normal elements managed by Tapestry but should be ignored (not sent to the server in a POST or Ajax update).

              return if name is ""
    
              existing = result[name]
    
              if _.isArray existing
                existing.push value
                return
    
              if existing
                result[name] = [existing, value]
                return
    
              result[name] = value
    
          return result
    
    
        defaultValidateAndSubmit = ->
    
          where = -> "processing form submission"
    
          try
    
            if ((@attr "data-validate") is "submit") and
               (not @meta SKIP_VALIDATION)
    
              @meta SKIP_VALIDATION, null
    
              hasError = false
              focusField = null
    
              for field in @find "[data-validation]"
                memo = {}
                where = -> "triggering #{events.field.inputValidation} event on #{field.toString()}"
                field.trigger events.field.inputValidation, memo
    
                if memo.error
                  hasError = true
                  focusField = field unless focusField
  • ¶

    Only do form validation if all individual field validation was successful.

              unless hasError
                memo = {}
                where = -> "trigging cross-form validation event"
                @trigger events.form.validate, memo
    
                hasError = memo.error
    
              if hasError
                clearSubmittingHidden this
  • ¶

    If a specific field has been identified as the source of the validation error, then focus on it.

                focusField.focus() if focusField
  • ¶

    Trigger an event to inform that form validation results in error

                where = -> "triggering validation in error event"
                @trigger events.form.validateInError
  • ¶

    Cancel the original submit event when there’s an error

                return false
  • ¶

    Allow certain types of elements to do last-moment set up. Basically, this is for FormFragment, or similar, to make their hidden field enabled or disabled to match their UI’s visible/hidden status. This is assumed to work or throw an exception; there is no memo.

            where = -> "triggering #{events.form.prepareForSubmit} event (after validation)"
    
            @trigger events.form.prepareForSubmit
    
          catch error
            console.error "Form validiation/submit error `#{error.toString()}', in form #{this.toString()}, #{where()}"
            console.error error
            return false
  • ¶

    Otherwise, the event is good, there are no validation problems, let the normal processing commence. Possibly, the document event handler provided by the t5/core/zone module will intercept form submission if this is an Ajax submission.

          return
    
        dom.onDocument "submit", TAPESTRY_CORE_FORM_SELECTOR, defaultValidateAndSubmit
  • ¶

    On any click on a submit or image, update the containing form to indicate that the element was responsible for the eventual submit; this is very important to Ajax updates, otherwise the information about which control triggered the submit gets lost.

        dom.onDocument "click", TAPESTRY_CORE_FORM_SELECTOR + " input[type=submit], " + TAPESTRY_CORE_FORM_SELECTOR + " input[type=image]", ->
          setSubmittingHidden (dom @element.form), this
          return
  • ¶

    Support for link submits. data-submit-mode will be non-null, possibly “cancel”. Update the hidden field, but also cancel the default behavior for the click.

        dom.onDocument "click", "a[data-submit-mode]", ->
          form = @findParent "form"
    
          unless form
            console.error "Submitting link element not contained inside a form element."
            return false
    
          setSubmittingHidden form, @closest "a[data-submit-mode]"
  • ¶

    Now the ugly part; if we just invoke submit() on the form, it does not trigger the form’s “submit” event, which we need.

          form.trigger "submit"
  • ¶

    And cancel the default behavior for the original click event

          return false
    
        exports =
          gatherParameters: gatherParameters
    
          setSubmittingElement: setSubmittingHidden
  • ¶

    Sets a flag on the form to indicate that client-side validation should be bypassed. This is typically associated with submit buttons that “cancel” the form.

          skipValidation: (form) ->
            form.meta SKIP_VALIDATION, true