- Jump To … +
  <<< back to documentation base.js constraint.js defaults.js factory.js field.js form.js main.js multiple.js pubsub.js remote.js ui.js utils.js validator.js validator_registry.js

- # form.js

- ¶

      import $ from 'jquery';
      import Base from './base';
      import Utils from './utils';

      var Form = function (element, domOptions, options) {
        this.__class__ = 'Form';

        this.element = element;
        this.$element = $(element);
        this.domOptions = domOptions;
        this.options = options;
        this.parent = window.Parsley;

        this.fields = [];
        this.validationResult = null;
      };

      var statusMapping = {pending: null, resolved: true, rejected: false};

      Form.prototype = {
        onSubmitValidate: function (event) {

- ¶

  This is a Parsley generated submit event, do not validate, do not prevent, simply exit and keep normal behavior

          if (true === event.parsley)
            return;

- ¶

  If we didn’t come here through a submit button, use the first one in the form

          var submitSource = this._submitSource || this.$element.find(Utils._SubmitSelector)[0];
          this._submitSource = null;
          this.$element.find('.parsley-synthetic-submit-button').prop('disabled', true);
          if (submitSource && null !== submitSource.getAttribute('formnovalidate'))
            return;

          window.Parsley._remoteCache = {};

          var promise = this.whenValidate({event});

          if ('resolved' === promise.state() && false !== this._trigger('submit')) {

- ¶

  All good, let event go through. We make this distinction because browsers differ in their handling of `submit` being called from inside a submit event [#1047]

          } else {

- ¶

  Rejected or pending: cancel this submit

            event.stopImmediatePropagation();
            event.preventDefault();
            if ('pending' === promise.state())
              promise.done(() => { this._submit(submitSource); });
          }
        },

        onSubmitButton: function(event) {
          this._submitSource = event.currentTarget;
        },

- ¶

  internal _submit submits the form, this time without going through the validations. Care must be taken to “fake” the actual submit button being clicked.

        _submit: function (submitSource) {
          if (false === this._trigger('submit'))
            return;

- ¶

  Add submit button’s data

          if (submitSource) {
            var $synthetic = this.$element.find('.parsley-synthetic-submit-button').prop('disabled', false);
            if (0 === $synthetic.length)
              $synthetic = $('<input class="parsley-synthetic-submit-button" type="hidden">').appendTo(this.$element);
            $synthetic.attr({
              name: submitSource.getAttribute('name'),
              value: submitSource.getAttribute('value')
            });
          }

          this.$element.trigger(Object.assign($.Event('submit'), {parsley: true}));
        },

- ¶

  Performs validation on fields while triggering events. @returns `true` if all validations succeeds, `false` if a failure is immediately detected, or `null` if dependant on a promise. Consider using `whenValidate` instead.

        validate: function (options) {
          if (arguments.length >= 1 && !$.isPlainObject(options)) {
            Utils.warnOnce('Calling validate on a parsley form without passing arguments as an object is deprecated.');
            var [group, force, event] = arguments;
            options = {group, force, event};
          }
          return statusMapping[ this.whenValidate(options).state() ];
        },

        whenValidate: function ({group, force, event} = {}) {
          this.submitEvent = event;
          if (event) {
            this.submitEvent = Object.assign({}, event, {preventDefault: () => {
              Utils.warnOnce("Using `this.submitEvent.preventDefault()` is deprecated; instead, call `this.validationResult = false`");
              this.validationResult = false;
            }});
          }
          this.validationResult = true;

- ¶

  fire validate event to eventually modify things before every validation

          this._trigger('validate');

- ¶

  Refresh form DOM options and form’s fields that could have changed

          this._refreshFields();

          var promises = this._withoutReactualizingFormOptions(() => {
            return $.map(this.fields, field => field.whenValidate({force, group}));
          });

          return Utils.all(promises)
            .done(  () => { this._trigger('success'); })
            .fail(  () => {
              this.validationResult = false;
              this.focus();
              this._trigger('error');
            })
            .always(() => { this._trigger('validated'); })
            .pipe(...this._pipeAccordingToValidationResult());
        },

- ¶

  Iterate over refreshed fields, and stop on first failure. Returns `true` if all fields are valid, `false` if a failure is detected or `null` if the result depends on an unresolved promise. Prefer using `whenValid` instead.

        isValid: function (options) {
          if (arguments.length >= 1 && !$.isPlainObject(options)) {
            Utils.warnOnce('Calling isValid on a parsley form without passing arguments as an object is deprecated.');
            var [group, force] = arguments;
            options = {group, force};
          }
          return statusMapping[ this.whenValid(options).state() ];
        },

- ¶

  Iterate over refreshed fields and validate them. Returns a promise. A validation that immediately fails will interrupt the validations.

        whenValid: function ({group, force} = {}) {
          this._refreshFields();

          var promises = this._withoutReactualizingFormOptions(() => {
            return $.map(this.fields, field => field.whenValid({group, force}));
          });
          return Utils.all(promises);
        },

        refresh: function() {
          this._refreshFields();
          return this;
        },

- ¶

  Reset UI

        reset: function () {

- ¶

  Form case: emit a reset event for each field

          for (var i = 0; i < this.fields.length; i++)
            this.fields[i].reset();

          this._trigger('reset');
        },

- ¶

  Destroy Parsley instance (+ UI)

        destroy: function () {

- ¶

  Field case: emit destroy event to clean UI and then destroy stored instance

          this._destroyUI();

- ¶

  Form case: destroy all its fields and then destroy stored instance

          for (var i = 0; i < this.fields.length; i++)
            this.fields[i].destroy();

          this.$element.removeData('Parsley');
          this._trigger('destroy');
        },

        _refreshFields: function () {
          return this.actualizeOptions()._bindFields();
        },

        _bindFields: function () {
          var oldFields = this.fields;

          this.fields = [];
          this.fieldsMappedById = {};

          this._withoutReactualizingFormOptions(() => {
            this.$element
            .find(this.options.inputs)
            .not(this.options.excluded)
            .each((_, element) => {
              var fieldInstance = new window.Parsley.Factory(element, {}, this);

- ¶

  Only add valid and not excluded `Field` and `FieldMultiple` children

              if (('Field' === fieldInstance.__class__ || 'FieldMultiple' === fieldInstance.__class__) && (true !== fieldInstance.options.excluded)) {
                let uniqueId = fieldInstance.__class__ + '-' + fieldInstance.__id__;
                if ('undefined' === typeof this.fieldsMappedById[uniqueId]) {
                  this.fieldsMappedById[uniqueId] = fieldInstance;
                  this.fields.push(fieldInstance);
                }
              }
            });

            $.each(Utils.difference(oldFields, this.fields), (_, field) => {
              field.reset();
            });
          });
          return this;
        },

- ¶

  Internal only. Looping on a form’s fields to do validation or similar will trigger reactualizing options on all of them, which in turn will reactualize the form’s options. To avoid calling actualizeOptions so many times on the form for nothing, _withoutReactualizingFormOptions temporarily disables the method actualizeOptions on this form while `fn` is called.

        _withoutReactualizingFormOptions: function (fn) {
          var oldActualizeOptions = this.actualizeOptions;
          this.actualizeOptions = function () { return this; };
          var result = fn();
          this.actualizeOptions = oldActualizeOptions;
          return result;
        },

- ¶

  Internal only. Shortcut to trigger an event Returns true iff event is not interrupted and default not prevented.

        _trigger: function (eventName) {
          return this.trigger('form:' + eventName);
        }

      };

      export default Form;