{"action":"created","issue":{"url":"https://api.github.com/repos/jquery-validation/jquery-validation/issues/1193","repository_url":"https://api.github.com/repos/jquery-validation/jquery-validation","labels_url":"https://api.github.com/repos/jquery-validation/jquery-validation/issues/1193/labels{/name}","comments_url":"https://api.github.com/repos/jquery-validation/jquery-validation/issues/1193/comments","events_url":"https://api.github.com/repos/jquery-validation/jquery-validation/issues/1193/events","html_url":"https://github.com/jquery-validation/jquery-validation/issues/1193","id":36718310,"number":1193,"title":"Add a callback handler to the remote method","user":{"login":"davesag","id":387098,"avatar_url":"https://avatars0.githubusercontent.com/u/387098?v=4","gravatar_id":"","url":"https://api.github.com/users/davesag","html_url":"https://github.com/davesag","followers_url":"https://api.github.com/users/davesag/followers","following_url":"https://api.github.com/users/davesag/following{/other_user}","gists_url":"https://api.github.com/users/davesag/gists{/gist_id}","starred_url":"https://api.github.com/users/davesag/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davesag/subscriptions","organizations_url":"https://api.github.com/users/davesag/orgs","repos_url":"https://api.github.com/users/davesag/repos","events_url":"https://api.github.com/users/davesag/events{/privacy}","received_events_url":"https://api.github.com/users/davesag/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":10,"created_at":"2014-06-28T05:00:27Z","updated_at":"2018-03-19T23:21:44Z","closed_at":"2015-06-28T19:53:16Z","author_association":"NONE","body":"I am using the jQuery Validation plugin to validate my user's postcode against my server-side database of postcodes using the `remote` method.\n\n```\nvar val = $(\"form\").validate({\n  rules: {\n    postcode: {\n      remote: {\n        url: '/locations/validatePostcodeExists',\n        type: 'POST',\n        dataType:\"json\",\n        data: {\n          country: function() { return $(\"#country\").val(); }\n        }\n      }\n    }\n  },\n  messages: {\n    postcode: \"Invalid Postcode\",\n  }\n});\n```\n\nI am also updating the list of valid suburbs based on the postcode provided, triggered by a change event off the postcode field.\n\n```\n$('#postcode').change(function(evt){\n  if ($(\"postcode\").valid()) {\n    getLocationsForPostcode($(\"#country\").val(), parseInt($(evt.target).val()));\n  }\n});\n\nfunction getLocationsForPostcode(country, postcode) {\n  $.ajax({\n    type: 'GET',\n    url: '/locations/forPostcode/' + country + '/' + postcode,\n    statusCode: {\n      400: function(err) {\n        console.debug('status 400 error', err);\n        // this should be caught by the validation handler first.\n      },\n      500: function(err) {\n        console.debug('status 500 error', err);\n        // this should be caught by the validation handler first.\n      }\n    },\n    success: function(data) {\n      console.debug('Found locations.', data.locations);\n      $('#subnationalDivision').val(data.locations[0].state);\n      var $burb = $(\"#suburb\");\n      $burb.empty();\n      if (data.locations.length > 1) {\n        $burb.append(\"<option value=''>Choose…</option>\");\n      }\n      for (var i = 0; i < data.locations.length; i++) {\n        $burb.append(\"<option value='\" + data.locations[i].id + \"'>\" + data.locations[i].name + \"</option>\");\n      }\n    }\n  });\n}\n```\n\nThe problem is that when the user enters a bad postcode and triggers the change event, even though the validation event is correctly fired (the form displays \"invalid postcode\"), the `change` event is also fired at the same time, before the remote validation's ajax request completes.  I need my change method to wait for that remote validation to complete but there doesn't seem to be a way to attach a callback handler to the remote event.\n\nLooking at [the code for the remote method](https://github.com/jzaefferer/jquery-validation/blob/master/src/core.js#L1252) it seems there is no way, short of rewriting the remote method, to allow the attachment of a callback.\n\nThe validator object itself exposes a `pending` hash that I would have expected would be have some content (and there's also a `pendingRequest` counter I'd have expected to be non-zero) if the `remote` had not yet completed, but on inspection that's not the case, so I can't use that to tell if the `remote` event has completed.\n\nI  see the completion of the `remote` validation sets `aria-invalid` to 'false' within the `postcode` field once it's completed.  At the time my `change` event is triggered this is not yet set.\n\nSo I tried using a `MutationObserver` to fire on the change to `aria-invalid`, but unfortunately the fact that the postcode also validates on `required` and `minlength = \"4\"` means that the `aria-invalid` attribute is still changing before the `remote` event completes.\n\n```\nvar MutationObserver = (window.MutationObserver || window.WebKitMutationObserver),\n    validationObserver = new MutationObserver(function(mutations){\n      var node, $node, nodeIndex;\n      for (nodeIndex in mutations) {\n        node = mutations[nodeIndex];\n        if (node.attributeName === \"aria-invalid\") {\n          $node = $(node.target);\n          console.debug(\"postcode\", $node.val(), $node.attr(\"aria-invalid\"));\n        }\n      }\n    });\nvalidationObserver.observe($(\"#postcode\").get(0), {attributes: true});\n```\n\nUnfortunately there seems to be no way to tell what trigered the attribute change so this approach is no use to me.\n\nSo finally this led me to create my own `postcode` validation method that manually makes the Ajax call, and duplicates much of the logic of the validator's own `remote` method.\n\nSo the the validation rules become\n\n```\nrules: {\n  postcode: {\n    postcode: true\n  }\n},\n```\n\nand I can drop the custom message for it as it's injected later.\n\nThe `postcode` rule is defined as follows:\n\n```\n$.validator.addMethod('postcode', function(value, element){\n  var validator = this,\n      previous = validator.previousValue( element );\n  if ( previous.old === value ) {\n    return previous.valid;\n  }\n  previous.old = value;\n  validator.startRequest( element );\n  $.ajax({\n    url: '/locations/validatePostcodeExists',\n    type: 'POST',\n    dataType:\"json\",\n    data: {\n      postcode: value,\n      country: function() { return $(\"#country\").val(); }\n    },\n    success: function(isValid) {\n      console.debug(\"validation returned\", isValid, validator);\n      if (true === isValid) {\n        var submitted = validator.formSubmitted;\n        validator.prepareElement( element );\n        validator.formSubmitted = submitted;\n        validator.successList.push( element );\n        delete validator.invalid[ element.name ];\n        validator.showErrors();\n        getLocationsForPostcode($(\"#country\").val(), value);\n      } else {\n        var errors = {};\n        errors[ element.name ] = validator.defaultMessage( element, \"postcode\" );\n        validator.invalid[ element.name ] = true;\n        validator.showErrors( errors );\n        $(\"#subnationalDivision\").val(\"\");\n        $(\"#suburb\").empty();\n        element.focus();\n      }\n      previous.valid = isValid;\n      validator.stopRequest( element, isValid );\n    }\n  });\n  return \"pending\";\n}, $.validator.format(\"Invalid Postcode\"));\n```\n\nThis works perfectly, but I can't help but feel that it could be vastly simplified via the simple addition of a `callback` handler to the original `remote` method. E.g:\n\n```\nrules: {\n  postcode: {\n    remote: {\n      url: '/locations/validatePostcodeExists',\n      type: 'POST',\n      dataType:\"json\",\n      data: {\n        country: function() { return $(\"#country\").val(); }\n      },\n      callback: function(element, value) {\n        getLocationsForPostcode($(\"#country\").val(), value);\n      }  \n    }\n  }\n},\n```\n\nDoes that make sense? It does seem to overload the validation with form mutation based on successful validation, but I imagine it's not an uncommon use-case to need to perform some sort of action given the successful validation of a field.\n\nAnother approach could be to add success / fail handler callbacks as a separate setting, e.g\n\n```\nrules: {\n  postcode: {\n    // whatever\n  }\n},\nmessages: {\n  postcode: {\n    // whatever\n  }\n},\ncallbacks: {\n  postcode: {\n    success: function(element, value) {\n      // whatever\n    },\n    fail: function(element, value, message {\n      // whatever\n    }\n  }\n}\n```\n\nI believe something along these lines would be very useful.\n\nDave\n"},"comment":{"url":"https://api.github.com/repos/jquery-validation/jquery-validation/issues/comments/374419117","html_url":"https://github.com/jquery-validation/jquery-validation/issues/1193#issuecomment-374419117","issue_url":"https://api.github.com/repos/jquery-validation/jquery-validation/issues/1193","id":374419117,"user":{"login":"brendanmc","id":10119934,"avatar_url":"https://avatars0.githubusercontent.com/u/10119934?v=4","gravatar_id":"","url":"https://api.github.com/users/brendanmc","html_url":"https://github.com/brendanmc","followers_url":"https://api.github.com/users/brendanmc/followers","following_url":"https://api.github.com/users/brendanmc/following{/other_user}","gists_url":"https://api.github.com/users/brendanmc/gists{/gist_id}","starred_url":"https://api.github.com/users/brendanmc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brendanmc/subscriptions","organizations_url":"https://api.github.com/users/brendanmc/orgs","repos_url":"https://api.github.com/users/brendanmc/repos","events_url":"https://api.github.com/users/brendanmc/events{/privacy}","received_events_url":"https://api.github.com/users/brendanmc/received_events","type":"User","site_admin":false},"created_at":"2018-03-19T23:21:44Z","updated_at":"2018-03-19T23:21:44Z","author_association":"NONE","body":"Sorry about the formatting in the post above. First time posting here and not sure how to format correctly :("}}
{"actor":{"display_login":"brendanmc"}}