Jump To …


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


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.


Module for logic relating to form input fields (input, select, textarea); specifically presenting validation errors and perfoming input validation when necessary.

define ["underscore", "./events", "./dom", "./utils", "./forms"],
  (_, events, dom, utils) ->

    ensureFieldId = (field) ->
      fieldId = field.attr "id"

      unless fieldId
        fieldId = _.uniqueId "field"
        field.attr "id", fieldId

      return fieldId

Finds any .help-block used for presenting errors for the provided field. Returns the found block(s) as an array of ElementWrapper. Returns null if no blocks can be found.

Normally, you would expect just a single help block for a field, but in some cases, such as to support responsive layout, there will be multiple help blocks for a single field.

  • field - element wrapper for the field
    findHelpBlocks = (field) ->
      fieldId = field.attr "id"

When the field has an id (the normal case!), search the body for the matching help blocks.

      if fieldId
        blocks = dom.body.find "[data-error-block-for='#{fieldId}']"

        return blocks if blocks.length > 0

Assign a unique (hopefully!) client id for the field, which will be used to link the field and the new help-block together.

        fieldId = ensureFieldId field

Not found by id, but see if an empty placeholder was provided within the same .form-group.

      group = field.findParent ".form-group"

      return null unless group

This happens less often, now that the Errors component ensures (at render time) a fieldId and a data-error-block-for element. Even so, sometimes a template will just contain a div.help-block[data-presentation=error]

      block = group.findFirst "[data-presentation=error]"

      if block
        block.attr "data-error-block-for", fieldId
        return [block]

Not found, so perhaps it will be created dynamically.

      return null

    createHelpBlock = (field) ->
      fieldId = ensureFieldId field

No containing group ... this is a problem, probably an old 5.3 application upgraded to 5.4 or beyond. Place the block just after the field.

      container = field.parent()

      block = dom.create "p",
        class: "help-block"
        "data-error-block-for": fieldId

The .input-group selectors are used to attach buttons or markers to the field. In which case, the help block can go after the group instead.

      if container.hasClass("input-group")
        container.insertAfter block
        field.insertAfter block

      return block

    showValidationError = (id, message) ->
      dom.wrap(id).trigger events.field.showValidationError, { message }

    collectOptionValues = (wrapper) ->
      _.pluck wrapper.element.options, "value"

Default registrations:

    dom.onDocument events.field.inputValidation, (event, formMemo) ->

Fields that are disbled, or not visible to the user are not subject to validation. Typically, a field will only be invisible due to the core/FormFragment component.

      return if @element.disabled or (not @deepVisible())

      failure = false

      fieldValue =
        if (@attr "data-value-mode") is "options"
          collectOptionValues this

      memo = value: fieldValue

      postEventTrigger = =>
        if memo.error

Assume the event handler displayed the message.

          failure = true

          if _.isString memo.error

            @trigger events.field.showValidationError, { message: memo.error }

      @trigger events.field.optional, memo


      unless failure or (utils.isBlank memo.value)

        @trigger events.field.translate, memo


        unless failure
            if _.isUndefined memo.translated
              memo.translated = memo.value

            @trigger events.field.validate, memo


      if failure
        formMemo.error = true
        @trigger events.field.clearValidationError


    dom.onDocument events.field.clearValidationError, ->
      blocks = exports.findHelpBlocks this

      for block in blocks or []
        block.parent().removeClass "has-error"

      group = @findParent ".form-group"

      group and group.removeClass "has-error"


    dom.onDocument events.field.showValidationError, (event, memo) ->
      blocks = exports.findHelpBlocks this

      unless blocks
        blocks = [exports.createHelpBlock this]

      for block in blocks

Add "has-error" to the help-block's immediate container; this assist with some layout issues where the help block can't be under the same .form-group element as the field (more common with a horizontal form layout).


      group = @findParent ".form-group"

      container = group or @parent().closest(":not(.input-group)")

      container.addClass "has-error"


    exports = {findHelpBlocks, createHelpBlock, showValidationError}