113 votes

Moyen générique de détecter si un formulaire html est édité

J'ai un formulaire html à onglets. Lorsque l'on passe d'un onglet à l'autre, les données de l'onglet en cours sont conservées (dans la base de données) même si elles ne sont pas modifiées.

Je voudrais que l'appel de persistance ne soit effectué que si le formulaire est modifié. Le formulaire peut contenir n'importe quel type de contrôle. L'édition du formulaire ne doit pas nécessairement se faire en tapant du texte, mais en choisissant une date dans un contrôle de calendrier.

Une façon d'y parvenir serait d'afficher le formulaire en mode lecture seule par défaut et de disposer d'un bouton "Modifier". Si l'utilisateur clique sur ce bouton, l'appel à la base de données est effectué (une fois de plus, indépendamment de la modification des données). C'est une meilleure amélioration par rapport à ce qui existe actuellement).

J'aimerais savoir comment écrire une fonction javascript générique qui vérifierait si l'une des valeurs des contrôles a été modifiée ?

0 votes

Un site solution intéressante a été publié par Craig Buckler sur SitePoint. Il est particulièrement intéressant de noter que la solution ne repose pas sur jQuery et qu'elle est compatible avec tous les navigateurs.

4voto

Teekin Points 4544

Voici comment je l'ai fait (sans utiliser jQuery).

Dans mon cas, je voulais qu'un élément de formulaire particulier ne soit pas compté, car c'est l'élément qui a déclenché la vérification et qui aura donc toujours changé. L'élément exceptionnel est nommé 'reporting_period' et est codé en dur dans la fonction 'hasFormChanged()'.

Pour tester, faites en sorte qu'un élément appelle la fonction "changeReportingPeriod()", que vous voudrez probablement nommer autrement.

IMPORTANT : Vous devez appeler setInitialValues() lorsque les valeurs ont été fixées à leur valeur initiale (généralement au chargement de la page, mais pas dans mon cas).

REMARQUE : Je ne prétends pas qu'il s'agit d'une solution élégante. En fait, je ne crois pas aux solutions JavaScript élégantes. En JavaScript, je mets l'accent sur la lisibilité et non sur l'élégance structurelle (comme si cela était possible en JavaScript). Je ne me préoccupe pas du tout de la taille des fichiers lorsque j'écris du JavaScript car c'est à cela que sert gzip, et essayer d'écrire du code JavaScript plus compact conduit invariablement à des problèmes intolérables de maintenance. Je ne présente aucune excuse, n'exprime aucun remords et refuse d'en débattre. C'est JavaScript. Désolé, je devais être clair afin de me convaincre que je devais prendre la peine de poster. Soyez heureux ! :)

    var initial_values = new Array();

    // Gets all form elements from the entire document.
    function getAllFormElements() {
        // Return variable.
        var all_form_elements = Array();

        // The form.
        var form_activity_report = document.getElementById('form_activity_report');

        // Different types of form elements.
        var inputs = form_activity_report.getElementsByTagName('input');
        var textareas = form_activity_report.getElementsByTagName('textarea');
        var selects = form_activity_report.getElementsByTagName('select');

        // We do it this way because we want to return an Array, not a NodeList.
        var i;
        for (i = 0; i < inputs.length; i++) {
            all_form_elements.push(inputs[i]);
        }
        for (i = 0; i < textareas.length; i++) {
            all_form_elements.push(textareas[i]);
        }
        for (i = 0; i < selects.length; i++) {
            all_form_elements.push(selects[i]);
        }

        return all_form_elements;
    }

    // Sets the initial values of every form element.
    function setInitialFormValues() {
        var inputs = getAllFormElements();
        for (var i = 0; i < inputs.length; i++) {
            initial_values.push(inputs[i].value);
        }
    }

    function hasFormChanged() {
        var has_changed = false;
        var elements = getAllFormElements();

        for (var i = 0; i < elements.length; i++) {
            if (elements[i].id != 'reporting_period' && elements[i].value != initial_values[i]) {
                has_changed = true;
                break;
            }
        }

        return has_changed;
    }

    function changeReportingPeriod() {
        alert(hasFormChanged());
    }

2voto

Patrick Roberts Points 405

Voici une démonstration d'une méthode polyfill en JavaScript natif qui utilise l'option FormData() API pour détecter les entrées de formulaire créées, mises à jour et supprimées. Vous pouvez vérifier si quelque chose a été modifié en utilisant HTMLFormElement#isChanged et obtenir un objet contenant les différences à partir d'un formulaire de réinitialisation en utilisant HTMLFormElement#changes (en supposant qu'ils ne sont pas masqués par un nom d'entrée) :

Object.defineProperties(HTMLFormElement.prototype, {
  isChanged: {
    configurable: true,
    get: function isChanged () {
      'use strict'

      var thisData = new FormData(this)
      var that = this.cloneNode(true)

      // avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
      HTMLFormElement.prototype.reset.call(that)

      var thatData = new FormData(that)

      const theseKeys = Array.from(thisData.keys())
      const thoseKeys = Array.from(thatData.keys())

      if (theseKeys.length !== thoseKeys.length) {
        return true
      }

      const allKeys = new Set(theseKeys.concat(thoseKeys))

      function unequal (value, index) {
        return value !== this[index]
      }

      for (const key of theseKeys) {
        const theseValues = thisData.getAll(key)
        const thoseValues = thatData.getAll(key)

        if (theseValues.length !== thoseValues.length) {
          return true
        }

        if (theseValues.some(unequal, thoseValues)) {
          return true
        }
      }

      return false
    }
  },
  changes: {
    configurable: true,
    get: function changes () {
      'use strict'

      var thisData = new FormData(this)
      var that = this.cloneNode(true)

      // avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
      HTMLFormElement.prototype.reset.call(that)

      var thatData = new FormData(that)

      const theseKeys = Array.from(thisData.keys())
      const thoseKeys = Array.from(thatData.keys())

      const created = new FormData()
      const deleted = new FormData()
      const updated = new FormData()

      const allKeys = new Set(theseKeys.concat(thoseKeys))

      function unequal (value, index) {
        return value !== this[index]
      }

      for (const key of allKeys) {
        const theseValues = thisData.getAll(key)
        const thoseValues = thatData.getAll(key)

        const createdValues = theseValues.slice(thoseValues.length)
        const deletedValues = thoseValues.slice(theseValues.length)

        const minLength = Math.min(theseValues.length, thoseValues.length)

        const updatedValues = theseValues.slice(0, minLength).filter(unequal, thoseValues)

        function append (value) {
          this.append(key, value)
        }

        createdValues.forEach(append, created)
        deletedValues.forEach(append, deleted)
        updatedValues.forEach(append, updated)
      }

      return {
        created: Array.from(created),
        deleted: Array.from(deleted),
        updated: Array.from(updated)
      }
    }
  }
})

document.querySelector('[value="Check"]').addEventListener('click', function () {
  if (this.form.isChanged) {
    console.log(this.form.changes)
  } else {
    console.log('unchanged')
  }
})

<form>
  <div>
    <label for="name">Text Input:</label>
    <input type="text" name="name" id="name" value="" tabindex="1" />
  </div>

  <div>
    <h4>Radio Button Choice</h4>

    <label for="radio-choice-1">Choice 1</label>
    <input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" />

    <label for="radio-choice-2">Choice 2</label>
    <input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" />
  </div>

  <div>
    <label for="select-choice">Select Dropdown Choice:</label>
    <select name="select-choice" id="select-choice">
      <option value="Choice 1">Choice 1</option>
      <option value="Choice 2">Choice 2</option>
      <option value="Choice 3">Choice 3</option>
    </select>
  </div>

  <div>
    <label for="textarea">Textarea:</label>
    <textarea cols="40" rows="8" name="textarea" id="textarea"></textarea>
  </div>

  <div>
    <label for="checkbox">Checkbox:</label>
    <input type="checkbox" name="checkbox" id="checkbox" />
  </div>

  <div>
    <input type="button" value="Check" />
  </div>
</form>

1voto

GriffMG Points 11

J'aime beaucoup la contribution de Teekin ci-dessus, et je l'ai mise en œuvre.

Cependant, je l'ai étendu pour permettre également les cases à cocher en utilisant un code comme celui-ci :

// Gets all form elements from the entire document.
function getAllFormElements() {
    // Return variable.
    var all_form_elements = Array();

    // The form.
    var Form = document.getElementById('frmCompDetls');

    // Different types of form elements.
    var inputs = Form.getElementsByTagName('input');
    var textareas = Form.getElementsByTagName('textarea');
    var selects = Form.getElementsByTagName('select');
    var checkboxes = Form.getElementsByTagName('CheckBox');

    // We do it this way because we want to return an Array, not a NodeList.
    var i;
    for (i = 0; i < inputs.length; i++) {
        all_form_elements.push(inputs[i]);
    }
    for (i = 0; i < textareas.length; i++) {
        all_form_elements.push(textareas[i]);
    }
    for (i = 0; i < selects.length; i++) {
        all_form_elements.push(selects[i]);
    }
    for (i = 0; i < checkboxes.length; i++) {
        all_form_elements.push(checkboxes[i]);
    }
    return all_form_elements;
}

// Sets the initial values of every form element.
function setInitialFormValues() {
    var inputs = getAllFormElements();
    for (var i = 0; i < inputs.length; i++) {
        if(inputs[i].type != "checkbox"){
            initial_values.push(inputs[i].value);
        }
        else
        {
            initial_values.push(inputs[i].checked);
        }
    }

}

function hasFormChanged() {
    var has_changed = false;
    var elements = getAllFormElements();
    var diffstring = ""
    for (var i = 0; i < elements.length; i++) {
        if (elements[i].type != "checkbox"){
            if (elements[i].value != initial_values[i]) {
                has_changed = true;
                //diffstring = diffstring + elements[i].value+" Was "+initial_values[i]+"\n";
                break;
            }
         }
         else
         {
            if (elements[i].checked != initial_values[i]) {
                has_changed = true;
                //diffstring = diffstring + elements[i].value+" Was "+initial_values[i]+"\n";
                break;
            }
         }
    }
    //alert(diffstring);
    return has_changed;
}

Le diffstring est juste un outil de débogage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X