[Seasar-user:17099] Re: [Teeda]xxxItemsにJavaScriptで行追加後、バリデーションエラーが発生すると追加した行がなくなる

浅野護 [E-MAIL ADDRESS DELETED]
2009年 3月 23日 (月) 16:45:00 JST


浅野です。
お世話になります。

返信ありがとうございました。
いただいたコメントを参考にvalidate.jsを修正し、以下に対応できるように拡張しました。(テスト不十分ですが)
・バリデーション実行はイベントが発生した項目のみ
・サブミット実行時にバリデーション実行

ただし、validate.jsを直接修正してしまったのですが、このまま使用してもライセンス的に問題ないのでしょうか?


参考までに修正したvalidate.jsも記載します。

[使用例]
Kumu.Validator.targets(['doTest']);     // サブミットボタン押下時にバリデーション実行
Kumu.Validator.validateAll(false);      // イベントが発生した項目のみ
KumuValidaterConf = {
    …
}

[validate.js]
 if (typeof(Kumu) == 'undefined') {
  Kumu = {};
}else{
  if (typeof(Kumu.Event) == 'undefined') {
    Kumu.dynamicLoad('event');
  }
}

if (typeof(Kumu.Validator) == 'undefined') {
  Kumu.Validator = {};
}

var KumuValidatorConf;

Kumu.Validator = Kumu.extend(Kumu.Validator, {
  _type : 'name',

  _validators : {},

  _resultObj : [],

  // add by m.asano 2009.3.23(start)
  _targetType : 'name',

  _targets : [],

  _targetsObj : [],

  _validateAll : true,
  // add by m.asano 2009.3.23(end)

  _createValidator : function(type){
    if(type.indexOf(':') > 1){
      var obj;
      var arr = type.split(':');
      eval('obj = new
Kumu.Validator.'+arr[0]+'Validator("'+arr[1].toString()+'");');
      return obj;
    }else{
      var obj;
      eval('obj = new Kumu.Validator.'+type+'Validator();');
      return obj;
    }
  },

  modeId : function(type){
    if(type){
      this._type = 'id';
    }
  },

  // addd by m.asano 2009.3.23(start)
  modeTargetId : function(type){
    if(type){
      this._targetType = 'id';
    }
  },

  targets : function(eventTargets){
    this._targets = eventTargets;
  },

  validateAll : function(flag){
    this._validateAll = flag;
  },

  _createTargetsObj : function(){
    if(this._targetsObj == null || this._targetsObj.length == 0){
      for(var i=0; i<this._targets.length; i++){
        var el = this._getElementsTarget(this._targets[i]);
        for(var j=0; j<el.length; j++){
          this._targetsObj.push(el[j]);
        }
      }
    }
  },

  _getElementsTarget : function(id){
    if(this._targetType == 'name'){
      return $n(id);
    }else if(this._targetType == 'id'){
      return [$i(id)];
    }
  },
  // addd by m.asano 2009.3.23(end)

  _getElements : function(id){
    if(this._type == 'name'){
      return $n(id);
    }else if(this._type == 'id'){
      return [$i(id)];
    }
  },

  _createChain : function(ids){
    var chain = this._validators[ids];
    var e = this._getElements(ids);
    if(!chain && e){
      chain = new Kumu.Validator.ValidatorChain(e[0]);
      this._validators[ids] = chain;
    }
    return chain;
  },

  _parse : function(validateConf){
    if(validateConf){
      for(var v in validateConf){
        var conf = validateConf[v];
        if(conf instanceof Array){
          //chain
          var chain = this._createChain(v);
          if(chain){
            for(var i = 0; i < conf.length; i++){
              var validator = this._createValidator(conf[i]);
              chain.add(validator);
            }
          }
        }else if(typeof(conf) == 'string'){
          if(conf == 'result'){
            var e = this._getElements(v);
            if(e){
              this._resultObj.push(e[0]);
            }
          }
        }else if(conf instanceof Object){
          var chain = this._createChain(v);
          if(chain){
            for(var ids in conf){
              var validator = this._createValidator(ids);
              validator.showResult = conf[ids];
              chain.add(validator);
            }
          }
        }
      }
    }
  },

  loadValidator : function(){
    if(KumuValidatorConf){
      this._regist(KumuValidatorConf);
      this._disabled();
    }
  },

  _disabled : function(){
    for(var i = 0; i < this._resultObj.length; i++){
      this._resultObj[i].disabled = true;
    }
  },

  _enabled : function(){
    for(var i = 0; i < this._resultObj.length; i++){
      this._resultObj[i].disabled = false;
    }
  },

  // changed by m.asano 2009.3.23
  //_validate : function(ev){
  _validate : function(ev, elem){
    var result = true;
    for(var v in this._validators){
      var validator = this._validators[v];
      // add by m.asano 2009.3.23(start)
      if(!this._validateAll && elem != null && elem.id !=
validator.element.id){
        continue;
      }
      // add by m.asano 2009.3.23(end)
      if(!validator.validate()){
        this._disabled();
        result = false;
      }
    }
    if(result){
      this._enabled();
    }
  },

  _regist : function(validateConf){
    this._parse(validateConf);
    for(var v in this._validators){
      var elements = this._getElements(v);
      elements.map(function(element){
        if (element.type) {
          switch (element.type.toLowerCase()) {
            case 'checkbox':
            case 'radio':
              // changed by m.asano 2009.3.23(start)
              //Kumu.Event.addEvent(element, 'click',
this._validate.bindScopeAsEventListener(this));
              if(this._targets == null || this._targets.length == 0){
                Kumu.Event.addEvent(element, 'click',
this._validate.bindScopeAsEventListener(this, element));
              }else{
                this._createTargetsObj();
                for (var i=0; i<this._targetsObj.length; i++){
                  Kumu.Event.addEvent(this._targetsObj[i], 'click',
this._validate.bindScopeAsEventListener(this, element));
                }
              }
              // changed by m.asano 2009.3.23(end)
              break;
            case 'password':
            case 'text':
            case 'textarea':
              // changed by m.asano 2009.3.23(start)
              //Kumu.Event.addEvent(element, 'keyup',
this._validate.bindScopeAsEventListener(this));
              if(this._targets == null || this._targets.length == 0){
                Kumu.Event.addEvent(element, 'keyup',
this._validate.bindScopeAsEventListener(this, element));
              }else{
                this._createTargetsObj();
                for (var i=0; i<this._targetsObj.length; i++){
                  Kumu.Event.addEvent(this._targetsObj[i], 'click',
this._validate.bindScopeAsEventListener(this, element));
                }
              }
              // changed by m.asano 2009.3.23(end)
              break;
            case 'select-one':
            case 'select-multiple':
              // changed by m.asano 2009.3.23(start)
              //Kumu.Event.addEvent(element, 'change',
this._validate.bindScopeAsEventListener(this));
              if(this._targets == null || this._targets.length == 0){
                Kumu.Event.addEvent(element, 'change',
this._validate.bindScopeAsEventListener(this, element));
              }else{
                this._createTargetsObj();
                for (var i=0; i<this._targetsObj.length; i++){
                  Kumu.Event.addEvent(this._targetsObj[i], 'click',
this._validate.bindScopeAsEventListener(this, element));
                }
              }
              // changed by m.asano 2009.3.23(end)
              break;
            }
        }
      }.bindScope(this));
    }
  }
});

Kumu.Validator.ValidatorBase = function(){
  this.result = false;
  this.showResult = function(element, result){
    this.result = result;
    if(result){
      element.style.background = '#ffffff';
    }else{
      element.style.background = '#ff4500';
    }
  }
};

Kumu.Validator.ValidatorChain = function(element){

  this.element = element;

  this.validators = [];

  this.add = function(validator){
    this.validators.push(validator);
  };

  this.validate = function(evt){
    this.result = false;
    for(var i = 0; i < this.validators.length; i++){
      var validator = this.validators[i];
      var result = validator.validate(this.element.value);
      validator.showResult(element, result);
      if(!result){
        return false;
      }
    }
    this.result = true;
    return true
  }
}

Kumu.Validator.RequiredValidator = function(){
  this.validate = function(v){
    if(v == null || v.length == 0){
      return false;
    }else{
      return true;
    }
  }
}
Kumu.Validator.RequiredValidator.prototype =  new
Kumu.Validator.ValidatorBase();

Kumu.Validator.RegularExpressionValidator = function(pattern){
  this.pattern = pattern;
  this.validate = function(v){
    var re = new RegExp(this.pattern);
    var v = v.toString();
    if(!v.match(re)){
      return false;
    }else{
      return true;
    }
  }
}
Kumu.Validator.RegularExpressionValidator.prototype =  new
Kumu.Validator.ValidatorBase();

Kumu.Validator.IntegerValidator = function(){
  this.validate = function(v){
    var re = /^[-]?([1-9]\d*)?\d$/;
    var v = v.toString();
    if(!v.match(re)){
      return false;
    }else{
      return true;
    }
  }
}
Kumu.Validator.IntegerValidator.prototype = new
Kumu.Validator.ValidatorBase();

Kumu.Validator.MinRangeValidator = function(value){
  this.value = value;
  this.validate = function(v){
    var v = v.toString();
    if(v > this.value){
      return true;
    }else{
      return false;
    }
  }
}
Kumu.Validator.MinRangeValidator.prototype = new
Kumu.Validator.ValidatorBase();

Kumu.Validator.MaxRangeValidator = function(value){
  this.value = value;
  this.validate = function(v){
    var v = v.toString();
    if(v < this.value){
      return true;
    }else{
      return false;
    }
  }
}
Kumu.Validator.MaxRangeValidator.prototype = new
Kumu.Validator.ValidatorBase();

Kumu.Validator.DoubleValidator = function(){
  this.validate = function(v){
    var re = /^[-]?([1-9]\d*)?\d(\.\d*)?$/;
    var v = v.toString();
    if(!v.match(re)){
      return false;
    }else{
      return true;
    }
  }
}
Kumu.Validator.DoubleValidator.prototype = new
Kumu.Validator.ValidatorBase();

Kumu.Validator.MinLengthValidator = function(value){
  this.value = value;
  this.validate = function(v){
    var v = v.toString();
    if(v.length > this.value){
      return true;
    }else{
      return false;
    }
  }
}
Kumu.Validator.MinLengthValidator.prototype = new
Kumu.Validator.ValidatorBase();

Kumu.Validator.MaxLengthValidator = function(value){
  this.value = value;
  this.validate = function(v){
    var v = v.toString();
    if(v.length < this.value){
      return true;
    }else{
      return false;
    }
  }
}
Kumu.Validator.MaxLengthValidator.prototype = new
Kumu.Validator.ValidatorBase();
Kumu.Event.addOnLoadEvent(Kumu.Validator.loadValidator.bindScope(Kumu.Validator));





2009/03/23 12:33 <[E-MAIL ADDRESS DELETED]>:

> 米林です。
>
> 単純にaaa項目でエラーになった場合に、それ以降を
> バリデーションチェックしないのであれば、_validate
> 関数を拡張すれば良いです。
>
>  _validate : function(){
>   var result = true;
>   for(var v in this._validators){
>     var validator = this._validators[v];
>     if(!validator.validate()){
>       this._disabled();
>       result = false;
>       return; // 以降は処理しない
>     }
>   }
>   if(result){
>     this._enabled();
>   }
>  },
>
> 試していないので恐縮ですが、よろしくお願いします。
>
>
> ----- Original Message ----- From: "浅野護" <[E-MAIL ADDRESS DELETED]>
>
>
>  浅野です。お世話になります。
>>
>> 返信ありがとうございます。
>>
>> 説明不足で「入力した項目だけをバリデーションチェックする」が正確に伝わりませんでした。すみません。
>>
>> 例えば、
>> function test1(element, result){
>>   if(result){
>>       element.style.background = '#ffffff';
>>   }else{
>>       element.style.background = '#ffc0cb';
>>       alert("値を入力してください");
>>   }
>> }
>> (test2省略)
>> KumuValidatorConf = {
>>   aaa : {'Required' : test1},
>>   bbb : {'MaxLength:3' : test2}
>> }
>>
>> のようにしたとき、aaa項目(テキストフィールド)のkeyupイベントが発生したタイミングで、aaaとbbbのバリデーションチェックが実行されているように見受けられました。(bbbは未入力)
>> よって、aaa項目(イベントが発生した項目)に対するバリデーションチェックのみ実行したいのですが、可能でしょうか?
>> (コールバック関数(test1, test2)内で、エラー時にalert文を表示していることが関係しているのでしょうか?)
>>
>> また、教えていただいたサブミット時にバリデーションチェックする方法も試しました。
>>
>> 上記が関係すると思うのですが、aaaのチェック時にaaaとbbbのバリデーションチェックが実行され、bbbのチェック時にもaaaとbbbのバリデーションチェックが実行されてしまいます。
>> (上記が解決すれば、こちらも解決すると思います。)
>>
>> よろしくお願いいたします。
>>
>>
>> 2009/03/23 8:15 <[E-MAIL ADDRESS DELETED]>:
>>
>>  米林です。
>>>
>>>
>>> KumuValidatorConfで設定したバリデーションが実行されるタイミングは、(サブミット時ではなく)各項目に値が入力されたタイミング(?)と認識しています。
>>>
>>>>
>>>>
>>> 現在、バリデーションが実行されるタイミングは
>>> validate.js内の_regist関数内で以下のように設定されています。
>>> -checkbox/radioはclick時
>>> -password/text/textareaはkeyup時
>>> -select-one/select-multipleはchange時
>>>
>>>  例えば以下のようなことは可能でしょうか?
>>>
>>>> ・入力した項目だけをバリデーションチェック実行する。
>>>> もしくは
>>>> ・サブミット時にバリデーションチェック実行する。
>>>>
>>>>
>>> 入力項目だけをバリデーションチェックするのであれば
>>> 独自バリデータを自作して入力がある場合のみバリデーションを
>>> 実行すれば良いと思います。
>>> 以下のValidatorの自作参照
>>> http://teeda.seasar.org/ja/kumu_validate.html
>>> 例)入力があった場合のみ実行
>>> Kumu.Validator.FooValidator = function(){
>>>  this.validate = function(v){
>>>  if(v == null || v.length == 0){
>>>    return true;
>>>  }else{
>>>    var re = /^[-]?\d*$/;
>>>    var v = v.toString();
>>>    if(!v.match(re)){
>>>      return false;
>>>    }else{
>>>      return true;
>>>    }
>>>  }
>>>  }
>>> }
>>> Kumu.Validator.FooValidator.prototype = new
>>> Kumu.Validator.ValidatorBase();
>>> KumuValidatorConf = {
>>>  test : ['Foo'],
>>> }
>>>
>>> また、あるボタンをクリックした際にだけバリデーションを適用するには
>>> _regist関数を以下のようにすればいけると思います。
>>>
>>>  _regist : function(validateConf){
>>>  this._parse(validateConf);
>>>  Kumu.Event.addEvent(document.フォーム要素.ボタン要素, 'click',
>>>    this._validate.bindScopeAsEventListener(this));
>>>  }
>>>
>>> 汎用的な対応になっていませんが、validate.jsをカスタマイズすれば
>>> 対応は可能だと思います。
>>>
>>> また浅野さんが作成されているalertが表示されるものの簡単な
>>> サンプルを見せて頂けると何かしら方法が提示出来るかもしれません。
>>>
>>>
>>> 宜しくお願いします。
>>>
>>> ----- Original Message ----- From: "浅野護" <[E-MAIL ADDRESS DELETED]>
>>>
>>>
>>>
>>>  浅野です。お世話になります。
>>>
>>>>
>>>> 返信ありがとうございました。
>>>>
>>>> 結局、クライアント側でもバリデーションを行うことを検討しているのですが、KumuのValidatorの動作について教えてください。
>>>>
>>>>
>>>> KumuValidatorConfで設定したバリデーションが実行されるタイミングは、(サブミット時ではなく)各項目に値が入力されたタイミング(?)と認識しています。
>>>>
>>>>
>>>> バリデーションのコールバック関数を設定し、エラー時にはalert文で、エラーメッセージを表示しようとしたのですが、1つのバリデーションチェックが実行されると、まだ未入力の他の項目のバリデーションチェックも実行されます。
>>>> よって、まだ未入力項目のエラーメッセージダイアログも表示されてしまうので、この回避策を調査しています。
>>>>
>>>> 例えば以下のようなことは可能でしょうか?
>>>> ・入力した項目だけをバリデーションチェック実行する。
>>>> もしくは
>>>> ・サブミット時にバリデーションチェック実行する。
>>>>
>>>> よろしくお願いいたします。
>>>>
>>>>
>>>> 2009/03/19 16:00 Koichi Kobayashi <[E-MAIL ADDRESS DELETED]>:
>>>>
>>>>  小林 (koichik) です.
>>>>
>>>>>
>>>>> Date:    Thu, 19 Mar 2009 12:04:53 +0900
>>>>> From:    浅野護 <[E-MAIL ADDRESS DELETED]>
>>>>> To:      [E-MAIL ADDRESS DELETED]
>>>>> Subject: [Seasar-user:17079]
>>>>> [Teeda]xxxItemsにJavaScriptで行追加後、バリデーションエラーが発生すると追加した行がなくなる
>>>>>
>>>>> >
>>>>>
>>>>>
>>>>> 一覧に表示されているxxxItemsに対し、JavaScriptで行追加した後、登録などのdoメソッドでバリデーションエラーが発生すると、xxxItemsのサイズは元の状態に戻ります。
>>>>>
>>>>> do〜() メソッドが呼ばれた場合,Teeda としては
>>>>> バリデーションは終わっており,その後の描画は入力時の
>>>>> 状態ではなく,ページクラスの状態で描画されます.
>>>>>
>>>>> >
>>>>>
>>>>>
>>>>> 例えば、テキストフィールドなどに値を入力し、バリデーションエラーが発生しても、入力した値は保持されて再描画されるので、xxxItemsも変更が保持されるのかと思っていたのですが、これは仕様でしょうか?
>>>>>
>>>>> 実際には,xxxItems はサブミット時の状態を維持しています.
>>>>> サブミット時の状態というのは,xxxItemsSave によって
>>>>> 保持されている値であり,JavaScript で追加された
>>>>> 情報は反映されていません.
>>>>> JavaScript が追加する <input> や <select> 等は
>>>>> xxxItems そのものではないことに注意してください.
>>>>> それらは xxxItems の子コンポーネントに反映されます.
>>>>>
>>>>> xxxItems 自体に JavaScript で追加された情報が
>>>>> 反映されるのは UPDATE_MODEL_VALUES フェーズであり,
>>>>> これはバリデーションエラーが発生すると実行されません.
>>>>>
>>>>> http://teeda.seasar.org/ja/extension/concept/lifecycle.html#JSF
>>>>> ライフサイクルとの関係
>>>>>
>>>>> このため,xxxItems はサブミット時の状態すなわち
>>>>> 前の描画時の状態 (JavaScript により追加される
>>>>> 行を含まない状態) のままとなります.
>>>>>
>>>>> > 仕様の場合は、xxxItemsの変更を保持する方法はないでしょうか?
>>>>>
>>>>> 実際にはバリデーションでエラーになった場合でも
>>>>> xxxItems に JavaScript による変更を反映させる方法と
>>>>> いうことになりますが,いい方法が思いつきません.
>>>>> 他の方はどうやってるんでしょうね?
>>>>>
>>>>>
>>>>> 無理矢理な案としては,xxxItems のサイズをあらかじめ
>>>>> 増やしておくというのが考えられます.
>>>>> 少々面倒ですが,xxxItems の要素である Dto に
>>>>> 有効/無効を表すフラグを持たせて,
>>>>> # ページクラスにも必要です.
>>>>>
>>>>> public class XxxDto {
>>>>>  public boolean enable;
>>>>>  ...
>>>>> }
>>>>>
>>>>> xxxItems は実際に追加されうる上限のサイズになるように
>>>>> 予備の XxxDto を持たせます.
>>>>> 例えば最大 100 件まで増やせるなら,描画時には
>>>>> 10 件しかなくても xxxItems の要素数は 100 に
>>>>> なるように,予備の XxxDto を入れておきます.
>>>>> ただし,予備の XxxDto の enable フラグは false にします.
>>>>>
>>>>> HTML では,enable が false の場合は描画しないように
>>>>> します.
>>>>> また,enable を隠しフィールドに持たせます.
>>>>>
>>>>> <input type="hidden" id="xxxItemsSave" />
>>>>> <div id="xxxItems">
>>>>>  <input type="hidden" id="enable" />
>>>>>  <div id="isEnable">
>>>>>  ...
>>>>>  </div>
>>>>> </div>
>>>>>
>>>>> JavaScript で行を追加する際,enable の value は
>>>>> true にします.
>>>>>
>>>>> これでバリデーションエラーが発生した場合でも
>>>>> xxxItems は上限分のサイズを持っているので,
>>>>> 追加された行の分も描画されます.たぶん.
>>>>> 実際には試していないのでまずいところがあるかも
>>>>> しれませんが.
>>>>>
>>>>>
>>>>> 実際の案件で使ってる人の方がいい案を持ってるかも
>>>>> しれません.なにかあれば自分も知りたいです.
>>>>>
>>>>>
>>>>> --
>>>>> <component name="koichik">
>>>>>  <property name="fullName">"Koichi Kobayashi"</property>
>>>>>  <property name="email">"[E-MAIL ADDRESS DELETED]"</property>
>>>>>  <property name="blog">"http://d.hatena.ne.jp/koichik"</property>
>>>>> </component>
>>>>>
>>>>>
>>>>  ---
>>> 米林 正明
>>> http://www.abby.co.jp
>>>
>>
> ---
> 米 林 正 明
>
> http://www.abby.co.jp
> _______________________________________________
> Seasar-user mailing list
> [E-MAIL ADDRESS DELETED]
> https://ml.seasar.org/mailman/listinfo/seasar-user
>
-------------- next part --------------
HTMLの添付ファイルを保管しました...
URL: <http://ml.seasar.org/archives/seasar-user/attachments/20090323/32c3c347/attachment.html>


Seasar-user メーリングリストの案内