web-dev-qa-db-ja.com

jquery検証は、リモート検証がtrueを返すのを待たず、フォームが有効であると見なします

$("#new_component_form").validate({
  errorClass: 'input-error',
  rules : {
    "comp_data[account_name]" : {
      required: true,
      remote: {
        url: "/validate",
        data: {
          provider: 'Twitter'
        }
      }
    }
  },
  onsubmit: true,
  onfocusout: false,
  onkeyup: false,
  onclick: false
});



 $("#new_component_form").submit(function(){
    console.log($(this).valid());

値が無効な場合でも、trueを出力します。検証が最終的に失敗し、エラーメッセージが表示されますが、フォームはまだ送信されています。

22
oreoshake

JQuery Validate 1.11.1(およびおそらくそれより古い)の時点では、受け入れられた回答は機能しません。さらに、この質問に対する簡単な答えはありません。このソリューションでは、jQuery検証にカスタム検証メソッドを追加する必要があります。

実際、簡単な答えは次のようになります。フォームを送信するだけの場合は、手動でvalid()を呼び出さないでください。 Validateプラグインに任せてください。内部的には、フォームの送信を許可する前に、すべての非同期リクエストが完了するのを待ちます。この問題は、valid()またはelement()を手動でチェックしている場合にのみ発生します。

ただし、それを行う必要がある理由はたくさんあります。たとえば、私が作業しているページでは、フォームの残りの部分を有効にする前に、リモートバリデーターを使用してフィールドの有効性を確認する必要があります。 jQuery Validationを使用する代わりに手動で行うこともできますが、それは作業の重複です。

では、なぜ_async: false_の設定が機能しないのですか? asyncをfalseに設定すると、リクエストwillは同期的に行われますが、プラグインはこれを正しく処理しません。内部のremote関数は常に_"pending"_を返します。これにより、valid()関数はtrueを返しますリクエストがすでに完了して受信した場合でも) false response!応答の値をチェックしたり、後でエラーを表示したりしません。

同期コールバックを使用するときにvalid()element()を同期的に動作させるための解決策は、カスタム検証メソッドを追加することです。私はこれを自分で試しましたが、うまくいくようです。通常のリモート検証からソースコードをコピーし、同期ajax呼び出しを処理するように変更するだけで、デフォルトで同期することができます。

V1.11.1のリモート関数のソースコードは、jquery.validate.jsの1112行目から始まります。

_remote: function( value, element, param ) {
    if ( this.optional(element) ) {
        return "dependency-mismatch";
    }

    var previous = this.previousValue(element);
    if (!this.settings.messages[element.name] ) {
        this.settings.messages[element.name] = {};
    }
    previous.originalMessage = this.settings.messages[element.name].remote;
    this.settings.messages[element.name].remote = previous.message;

    param = typeof param === "string" && {url:param} || param;

    if ( previous.old === value ) {
        return previous.valid;
    }

    previous.old = value;
    var validator = this;
    this.startRequest(element);
    var data = {};
    data[element.name] = value;
    $.ajax($.extend(true, {
        url: param,
        mode: "abort",
        port: "validate" + element.name,
        dataType: "json",
        data: data,
        success: function( response ) {
            validator.settings.messages[element.name].remote = previous.originalMessage;
            var valid = response === true || response === "true";
            if ( valid ) {
                var submitted = validator.formSubmitted;
                validator.prepareElement(element);
                validator.formSubmitted = submitted;
                validator.successList.Push(element);
                delete validator.invalid[element.name];
                validator.showErrors();
            } else {
                var errors = {};
                var message = response || validator.defaultMessage( element, "remote" );
                errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
                validator.invalid[element.name] = true;
                validator.showErrors(errors);
            }
            previous.valid = valid;
            validator.stopRequest(element, valid);
        }
    }, param));
    return "pending";
}
_

Ajax呼び出しが完了した場合でも、常に「保留中」を返すことに注意してください。

この問題を修正するには、次の変更を行います。

  1. クロージャを作成するために、valid変数の宣言をajax呼び出しとsuccess関数の外に移動し、デフォルト値の「pending」を割り当てます。
  2. valid変数の古い宣言を割り当てに変更します。
  3. 定数「pending」の代わりにvalid変数を返します。

プラグインからプラグインへの完全なコードは次のとおりです。これをjsファイルとして保存し、jQuery検証のインクルード後にページまたはテンプレートにインクルードするだけです。

_//Created for jQuery Validation 1.11.1
$.validator.addMethod("synchronousRemote", function (value, element, param) {
    if (this.optional(element)) {
        return "dependency-mismatch";
    }

    var previous = this.previousValue(element);
    if (!this.settings.messages[element.name]) {
        this.settings.messages[element.name] = {};
    }
    previous.originalMessage = this.settings.messages[element.name].remote;
    this.settings.messages[element.name].remote = previous.message;

    param = typeof param === "string" && { url: param } || param;

    if (previous.old === value) {
        return previous.valid;
    }

    previous.old = value;
    var validator = this;
    this.startRequest(element);
    var data = {};
    data[element.name] = value;
    var valid = "pending";
    $.ajax($.extend(true, {
        url: param,
        async: false,
        mode: "abort",
        port: "validate" + element.name,
        dataType: "json",
        data: data,
        success: function (response) {
            validator.settings.messages[element.name].remote = previous.originalMessage;
            valid = response === true || response === "true";
            if (valid) {
                var submitted = validator.formSubmitted;
                validator.prepareElement(element);
                validator.formSubmitted = submitted;
                validator.successList.Push(element);
                delete validator.invalid[element.name];
                validator.showErrors();
            } else {
                var errors = {};
                var message = response || validator.defaultMessage(element, "remote");
                errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
                validator.invalid[element.name] = true;
                validator.showErrors(errors);
            }
            previous.valid = valid;
            validator.stopRequest(element, valid);
        }
    }, param));
    return valid;
}, "Please fix this field.");
_

私はこれを自分のフォームでテストしましたが、うまく機能します。フォームの残りの部分を有効にする前に、要素の有効性をテストできます。ただし、キーを押すたびに同期コールバックが実行されないように、_onkeyup: false_を設定することをお勧めします。私も_onfocusout: false_を使うのが好きです。

これを使用するには、検証設定の「remote」を、これを使用するすべての場所で「synchronousRemote」に置き換えるだけです。例えば:

_$("#someForm").validate({
    rules: {
        someField: {
            required: true,
            synchronousRemote: {
                    url: "/SomePath/ValidateSomeField"
                    //notice that async: false need not be specified. It's the default.
            }
        }
    },
    messages: {
        someField: {
            required: "SomeField is required.",
            synchronousRemote: "SomeField does not exist."
        }
    },
    onkeyup: false,
    onfocusout: false
});
_
28
Glazed

同じ問題にぶつかって、リモート呼び出しを同期に設定したようです-async:false

それ以外の場合、$("form").valid()はリモート検証に対してtrueを返します。以下の使用内容を参照してください。

rules: {
    NameToValidate: {
        required: true,
        remote: function()
        {
            return {
            type: "POST",
            async: false,
            url: "www.mysite.com/JSONStuff",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            data: JSON.stringify( {
                Name: "UNIQUE NAME"
            } )
        }
    }
},
.....
7
Bogdan

これが私のプロジェクトで思いついた解決策です。

var validPendingTimeout;

function doSomethingWithValid() {
  var isValid = $("#form").valid();
  var isPending = $("#form").validate().pendingRequest !== 0;

  if (isPending) {
    if (typeof validPendingTimeout !== "undefined") {
      clearTimeout(validPendingTimeout);
    }
    validPendingTimeout = setTimeout(doSomethingWithValid, 200);
  }

  if (isValid && !isPending) {
    // do something when valid and not pending
  } else {
    // do something else when not valid or pending
  }
}

これは、リモート検証がある場合は常に$( "#form")。validate()。pendingRequestが常に> 0になるという事実を利用しています。

注:これは、リモートリクエストでasync: trueを設定しても機能しません。

6
Paul B.

この問題を修正する最も簡単な方法(ここで報告されています: https://github.com/jzaefferer/jquery-validation/issues/361 )は、フォームが有効かどうかを2回確認することです。

_if (myForm.valid() && myForm.valid()) {
    ...
}
_

リモートルールを_async:false_に設定し、valid()を使用して有効性をチェックすると、実際には呼び出しが終了するのを待ちますが、それらの戻り値は使用されません。 valid()を再度呼び出すと、それらが考慮されます。

少し醜いですが、動作します。私はjquery2.1.0とjquery-validation1.11.1を使用しています。

6
emilast

検証でfalseが返された場合は、ブラウザの通常の送信を停止する必要があります。

$("#new_component_form").submit(function() {
    if ($(this).valid())) {
        console.log("I'm a valid form!");
    } else {
        console.log("I'm NOT a valid form!");
        //Stop the normal submit of the browser
        return false;
    }
}
0
ElHacker

$("#form").valid()$("#form").validate().pendingRequestの両方をチェックするカスタム関数を作成することでこれを解決しました。

_function formIsValid() {
    return $("#form").valid() && $("#form").validate().pendingRequest === 0;
}
_

これは、remote検証リクエスト中に$("#form").validate().pendingRequestが常に> 0になるという前提で機能します。これは、jquery-validation1.19.1を使用して機能します。

0
Gilbert

これがパッチであることはわかっていますが、現在のajaxが保留中であることを確認して修正しました

$.activeで現在のajax呼び出しを確認します。ajaxが実行されていない場合は、0が返されます。

$('#frmControlFamily #Identifier').valid();            
if ($.active == 0) {
    // my save logic
}
0

フォームを2回検証するか、現在のajax呼び出しが終了するかどうかを確認できます。

if (myForm.valid() && myForm.valid()) {

}

または

if ($.active == 0) {

}

これまでのところ、私が見つけた最良の解決策は、このスレッドの上部に記載されています。 https://stackoverflow.com/a/20750164/1106408

しかし、適切な解決策はありません。同じ形式で2つのリモート検証機能を使用すると、上記のすべてのアプローチが失敗します。

0
Muditha