0

Here is another issue I cannot quite believe hasn't been resolved yet... I am creating a registration form. Now... I need to do some ajax async validation. This is what I am doing right now (see code at the bottom).

It's great, it caches already checked values, it's async, etc. but:

  1. You are never 10000% sure it's validated before submission. This is a huge deal, as I ALSO check things on the server side. But still... making this code work "forcing" validation on submission turns it into a real monster!

  2. It seems like a lot of work for something that everything ought to be ready to go

  3. (Could be related to 2) I cannot think of a way to make this actually generic. I have another 10 fields which will need similar validation, but all validations differ a little, except the Ajax calls... and I am not cutting and pasting this code 10 times!

Am I missing something really obvious? Any ideas?

define([
'dojo/_base/declare',
'dijit/form/ValidationTextBox',
'dojo/_base/lang',
'app/globals',
], function(
 declare
, ValidationTextBox
, lang
, g
){
return declare('app.ValidationWorkspace', [ ValidationTextBox ], {

 ajaxSaidNo: {},
 ajaxSaidYes: {},
 ajaxRequested: {},

 onChange: function(value){
   this.validate();
 },

 validator: function(value){

   // console.log("Started validation for " + value);
   // Ajax has already said no -- returning false straight away
   if(this.ajaxSaidNo[value] ){
     this.invalidMessage = 'Workspace taken';
     return false;
   }

   // Run the normal field validators -- if they fail,
   // return false
   var validation =  Validators.workspace(value);
   if( ! validation.result){
     this.invalidMessage = validation.message;
     return false;

   // At this point, regexp validators passed and Ajax hasn't said "no".
   } else {
     // console.log("OK, ajasSaidYes for " + value + " is " +  this.ajaxSaidYes[value]); 
     if(! this.ajaxSaidYes[value] ){
       g.stores.workspacesAnon.query({name: value}).then(
         lang.hitch(this, function(res){
           if(res && res.length ){
             this.ajaxSaidNo[value] = true;
             //console.log("Added to Ajaxfailed: " + value);
             this.validate();
           } else {
             //console.log("Added to Ajaxsuccess: " + value);
             this.ajaxSaidYes[value] = true;
             this.validate();
           }
         })
       );    
     }  

     return true;
   }

 },

  invalidMessage: "Workspace name not valid",
  missingMessage: "Workspace name cannot be empty",

});

}
 );
4

2 に答える 2

1

TextBox 自体から自分自身を抽象化し、代わりにフォーム onSubmit をカスタマイズします。formDom.submit() の前に使用される一般的なコールバックは、単純に formDijit.validate() を呼び出し、そのブール値を form.submit に渡します (false の場合、もちろん送信は行われません)。

言い換えれば、バリデーターがブロッキング/アトミックに実行され、各バリデーター関数が戻り値を解決する前に戻らないことが期待されます。

あなたの状況では、dojo.Deferredが役に立ちます。form.onSubmit 中に ajax-requests から応答を取得していることに気付きます。「実行中」の ajax 呼び出しがすべて解決されたら、真の場合は送信を開始します。

<div tada-dojo-type="dijit.form.Form" id="form">
   <div data-dojo-type="app.ValidationWorkspace" name="inputfield" id="inputfield1"></div>
</div>

あなたのコードベースで見られない不足しているものを埋める例として、以下はこの簡略化に従います

<script>
    declare('app.ValidationWorkspace', [ ValidationTextBox ], {
         invalidMessageLocal : 'JS renders your entry invalid',
         invalidMessageServer: 'Server expects something different then you entered',
         invalidMessage: '',

         regExp: 'Foo*', // validates false if does not start with "Foo"

         dfdValidator: undefined,

         validator: function() {
              var curVal = this.get("value");
              if(new RegExp(this.regExp).test(curVal)) {
                    // client-side passes, run serverside
                    this.dfdValidator = dojo.xhrGet({
                         url: 'validate.sapi'+
                            '?field=' + this.inputNode.name +
                            '&value='+ value
                    });
                    // we cannot truly base anything form-wise here, but we might want
                    // to set a message (and show it, left out)
                    this.dfdValidator.then(dojo.hitch(this, function(response) {
                          this.invalidMessage = this.invalidMessageServer + " : " + response;
                    }));
              }
         }
    });
</script>

そして、フォーム onSubmit を次のように変更します。

<script>
     dojo.addOnLoad(function() {
       dijit.byId('form').onSubmit = function() {
        var immediate = true;
        var deferreds = [];
        dojo.some(dijit.byId('form')._getDescendantFormWidgets(), function(w) {
            if(w.validate) {
                var tmp = w.validate();
                if(!tmp) immediate = false;
                else { // check if it has servervalidation in flight
                     if(w.dfdValidator) {
                         // it does, store it so we can keep a count and 
                         // not submit before all has passed
                         deferreds.push(w.dfdValidator);
                     }
                }
            }
            return tmp; /* if false - one field failed - foreach(some) stops loop */
        }); // end dojo.some
        var form = this.domNode
        var submit = function() { 
               // logic to submit your values here
               store.put(dojo.formToJson(form));
        }
        // simply stop here if local validators fail
        if(immediate == false) {

             return false;

        } else if(deferreds.length > 0) {

             // do not act before all is done    
             var dfdCounter = new dojo.Deferred();
             dfdCounter.then(submit);
             var dfdCount = deferreds.length;
             var incrCount = 0;
             var delayed = true; // delayed validation
             dojo.forEach(deferred, function(serverValidatedComplete) {
                 serverValidatedComplete.then(function(serverresponse) {
                     incrCount++;
                     if(/REGEXPTOTESTRESPONSEFALSE/.test(serverresponse))
                          delayed = false;
                     if(dfdCount == incrCount && delayed == true) 
                     {    /* YES, all is done, any errors? */
                          dfdCounter.resolve(/* optional arguments here */); 
                     }
                 });
             });
        } else {
             // no server validators are running, local validation succeeds
             submit();
        }
        // never return true - or <form> will submit as HTML should be doing
        return false;
       } // end mutation
     });
</script>
于 2012-08-06T17:07:09.993 に答える
0

受け入れられた答えは正しい解決策かもしれません。ただし、ノンブロッキング ajax チェックのために、単純な mixin を作成することになりました。

define([
  'dojo/_base/declare',
  'dojo/_base/lang',
  'app/globals',
  ], function(
    declare
  ,lang
  , g
  ){
    return declare(null, {

  ajaxSaidNo: {},
  ajaxSaidYes: {},
  ajaxRequested: {},

  constructor: function(){

    // Declaring object variable in constructor to make sure that
    // they are not class-wide (since they will be in the prototype)
    this.ajaxSaidNo = {};
    this.ajaxSaidYes = {};
    this.ajaxRequested = {};
  },

  // Overloads the validator, adding extra stuff
  ajaxValidate: function(value, options){

    // Set some defaults
    options.ajaxInvalidMessage = options.ajaxInvalidMessage || "Value not allowed";
    options.ajaxStore = options.ajaxStore || null;
    options.ajaxFilterField = options.ajaxFilterField  || 'name';

    // No ajaxStore query available, return true
    if( ! options.ajaxStore ){
      return true;
    }

    // console.log("Started validation for " + value);
    // Ajax has already said no -- returning false straight away
    if(this.ajaxSaidNo[value] ){
      this.invalidMessage = options.ajaxInvalidMessage;
      return false;
    }

    // console.log("OK, ajasSaidYes for " + value + " is " +  this.ajaxSaidYes[value]); 
    if(! this.ajaxSaidYes[value] ){
      var filterObject = {};
      filterObject[options.ajaxFilterField] = value;
      options.ajaxStore.query( filterObject ).then(
        lang.hitch(this, function(res){
          if(res && res.length ){
            this.ajaxSaidNo[value] = true;
            //console.log("Added to Ajaxfailed: " + value);
            this.validate();
          } else {
            //console.log("Added to Ajaxsuccess: " + value);
            this.ajaxSaidYes[value] = true;
            this.validate();
          }
        })
      );    
    }  

    return true;
  }

});
  }
);

次に、テキストボックス(または実際には任意のフィールド)の検証機能で、最小限の機能を追加するだけで機能します:

    return this.ajaxValidate(value, {
       ajaxInvalidMessage: "Workspace taken",
       ajaxStore: g.stores.workspacesAnon,
       ajaxFilterField: 'name',
    });

それだけです...素晴らしく明確で、好きな分野に適用できます。(私のアプローチについて十分な励ましのコメントを受け取った場合、これを「受け入れられた」回答として作成します...)

メルク。

于 2012-08-07T04:27:27.050 に答える