クライアント側でJavaScriptを積極的に使用してWEBアプリケーションを作成した人は皆、小数点の問題に直面したと思います。 そして、この問題の解決策は一見簡単に思えますが、決して些細なことではありません。 ExtJSは、実装と管理が簡単なアプローチを使用します。数値フィールドは、セパレーターと見なされる文字を示し、数字と「-」を除く他の文字は禁止されています。 しかし、このアプローチには、1つの重大な欠点があるように思われます。複数のレイアウトが使用されている場合、数字キーパッドの小数点区切り文字は異なる文字に対応します。 これを修正する方法を以下に説明します。
最初からやり直すか、標準のExt.form.field.Numberでフィルタリングがどのように機能するかを見てみましょう。
ドキュメントによると、コンポーネントには
disableKeyFilterプロパティがあり、これは入力文字のフィルタリングを行い、Ext.form.field.Textテキストフィールドから継承します。 ソーステキストフィールドで上記のプロパティを探しましょう。 コードでの唯一の言及はinitEventsメソッドにあり、filterKeysハンドラーはkeypressイベントでハングします。 次に、テキストから数値フィールドまでクラスの階層を「ウォーク」し、オーバーライドされたfilterKeysメソッドを探します。何も検索せずに、見つかったものを選択します。 メソッド内では、原則として特別なことは何もありません。フィルタリングは、入力文字が正規表現に準拠しているかどうかをチェックすることであり、正規表現
はコンポーネントの
構成時に
指定できます。 次に
、数値フィールドの
ドキュメントを見て、設定中にmaskReパラメーターを設定できないことを確認します。 不可解に処理される方法に関してのみ設定できます。 数値フィールドのソースに移動すると、initComponent()メソッドに次のように表示されます。
if (me.disableKeyFilter !== true) { allowed = me.baseChars + ''; if (me.allowDecimals) { allowed += me.decimalSeparator; } if (me.minValue < 0) { allowed += '-'; } allowed = Ext.String.escapeRegex(allowed); me.maskRe = new RegExp('[' + allowed + ']'); if (me.autoStripChars) { me.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi'); } }
つまり、フィルタリングが意図的にオフにされていない場合、コンポーネントは指定された設定に基づいて独自にmaskReを作成します。 標準バージョンでは、入力文字は次の「.-0123456789」のいずれかでなければなりません。 実際、これがフィルター全体です。
プラグインブランク
プラグインがどのように書かれているか知らない人は、
ドキュメントを読みに行き
ます 。 残りについては、以下のコードに複雑なものはありません。
Ext.define('Ext.plugin.form.field.NumberInputFilter', { alias : 'plugin.numberinputfilter', extend : 'Ext.AbstractPlugin', init : function(field) { // , if (!(field && field.isXType('numberfield'))) { return; } Ext.apply(field, { // , // Ext.form.field.Text filterKeys : function(e){ if (e.ctrlKey && !e.altKey) { return; } var key = e.getKey(), charCode = String.fromCharCode(e.getCharCode()); if(Ext.isGecko && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){ return; } if(!Ext.isGecko && e.isSpecialKey() && !charCode){ return; } if(!this.maskRe.test(charCode)){ e.stopEvent(); } } }); } });
小数点区切り
最も一般的な区切り文字はピリオド「。」であるためです。 コンマ「、」。これらがデフォルトの区切り文字になります。 allowedDecimalSeparators構成プロパティをプラグイン、必要に応じてこのプロパティのデフォルト値を設定するコンストラクタ、およびfilterKeys()メソッドでの処理自体に追加しましょう。
Ext.define('Ext.plugin.form.field.NumberInputFilter', { alias : 'plugin.numberinputfilter', extend : 'Ext.AbstractPlugin', constructor : function(cfg) { cfg = cfg || {}; // Ext.applyIf(cfg, { allowedDecimalSeparators : ',.' }); Ext.apply(this, cfg); }, init : function(field) { // , if (!(field && field.isXType('numberfield'))) { return; } Ext.apply(field, { // filterKeys : function(e){ if (e.ctrlKey && !e.altKey) { return; } var key = e.getKey(), charCode = String.fromCharCode(e.getCharCode()); if(Ext.isGecko && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){ return; } if(!Ext.isGecko && e.isSpecialKey() && !charCode){ return; } // begin hack if (charCode != this.decimalSeparator && this.allowedDecimalSeparators.indexOf(charCode) != -1) { // , // , // charCode = this.decimalSeparator; if (Ext.isIE) { // IE e.browserEvent.keyCode = charCode.charCodeAt(0); } else if (Ext.isGecko) { // gecko- e.stopEvent(); // var newEvent = document.createEvent('KeyEvents'); // , // .. , // newEvent.initKeyEvent( e.browserEvent.type, e.browserEvent.bubbles, true, //cancellable e.browserEvent.view, e.browserEvent.ctrlKey, e.browserEvent.altKey, e.browserEvent.shiftKey, e.browserEvent.metaKey, 0, // keyCode charCode.charCodeAt(0) // charCode ); e.getTarget().dispatchEvent(newEvent); // , . return; } else if (Ext.isWebKit) { // e.stopEvent(); // webkit initKeyboardEvent , TextEvent if (this.maskRe.test(charCode)) { var newEvent = document.createEvent('TextEvent'); newEvent.initTextEvent( 'textInput', e.browserEvent.bubbles, true, e.browserEvent.view, charCode ); e.getTarget().dispatchEvent(newEvent); } return; } } // end hack if(!this.maskRe.test(charCode)){ e.stopEvent(); } } }); } });
これで、サポートされているセパレータのいずれかが正しいセパレータに置き換えられます。 ただし、区切り文字は必要な場所にいくつでも入力できます。 この誤解を修正してください。
入力値の擬似マスクのサポート
疑似マスクのサポートとは、無効な場所に有効な文字だけでなく、無効な文字を導入できないことを意味します。 たとえば、マイナス記号「-」は、数字の先頭または文字列内のいくつかの小数点記号ではありません。 前述のように、文字フィルタリングは正規表現を介して実行されます。 ただし、入力マスクをサポートするには、さらに何かが必要なので、フィールドに別のcheckValue()メソッドを追加します。このメソッドは、新しく入力された文字を引数として受け取り、受け取った値を擬似マスクと照合します。
数値の条件付き文字列表現は、小数点区切り文字の「前」と「後」の部分文字列に分割できます。 以下を確認します。
checkValue : function(newChar) { // input var raw = this.getRawValue(); if (Ext.isEmpty(raw)) { // , : // - // - "-", // - return (newChar == this.decimalSeparator || (this.minValue < 0) && newChar == '-') || /^\d$/.test(newChar); } // ,... if (raw.length == this.maxLength) { // ... return false; } if (newChar == this.decimalSeparator && (!this.allowDecimals || raw.indexOf(this.decimalSeparator) != -1)) { // ... , , // return false; } // raw += newChar; raw = raw.split(new RegExp(Ext.String.escapeRegex(this.decimalSeparator))); return (!raw[0] || this.intRe.test(raw[0])) && (!raw[1] || this.decRe.test(raw[1])); }
上記のコードは十分に詳細にコメントされており、intReとdecReは不明のままです。 これらは、それぞれ数値の整数部分と小数部分をチェックするための正規表現であり、フィールドに追加されたupdateDecimalPrecision()メソッドでプラグインが接続されたときに形成されます。
// decimalPrecision // updateDecimalPrecision : function(prec, force) { if (prec == this.decimalPrecision && force !== true) { return; } if (!Ext.isNumber(prec) || prec < 1) { // , this.allowDecimals = false; } else { this.decimalPrecision = prec; } // var intRe = '^'; if (this.minValue < 0) { intRe += '-?'; } // integerPrecision - decimalPrecision , // intRe += '\\d' + (Ext.isNumber(this.integerPrecision) ? '{1,' + this.integerPrecision + '}' : '+') + '$'; this.intRe = new RegExp(intRe); if (this.allowDecimals) { // this.decRe = new RegExp('^\\d{1,' + this.decimalPrecision + '}$'); } else { delete this.decRe; } }
説明したメソッドは、作成したプラグインのinit()メソッドの最後のフィールドで呼び出す必要があります。
結果のプラグインは非常に機能しますが、いくつかの不快な機能がないわけではありません。たとえば、入力された値は、小数点以下が割り当てられた数字を完全に占めるため、強調表示することで削除できません。 この状況に対処するために、テキストの選択した部分の処理をフィールドに追加することにより、checkValue()コードをわずかに改善します。 その結果、コードは次のようになります
checkValue : function(newChar) { // input var raw = this.getRawValue(); // dom- var el = this.inputEl.dom; // var start = getSelectionStart(el); var end = getSelectionEnd(el); if (start != end) { // raw = raw.substring(0, start) + raw.substring(end); } if (Ext.isEmpty(raw)) { // , : // - // - "-", // - return (newChar == this.decimalSeparator || (this.minValue < 0) && newChar == '-') || /^\d$/.test(newChar); } // ,... if (raw.length == this.maxLength) { // ... return false; } if (newChar == this.decimalSeparator && (!this.allowDecimals || raw.indexOf(this.decimalSeparator) != -1)) { // ... , , // return false; } // raw = raw.substring(0, start) + newChar + raw.substring(start); raw = raw.split(new RegExp(Ext.String.escapeRegex(this.decimalSeparator))); return (!raw[0] || this.intRe.test(raw[0])) && (!raw[1] || this.decRe.test(raw[1])); }
以上です。 念のため、プラグインの完全なコードとデモを提供します。
Ext.define('Ext.plugin.form.field.NumberInputFilter', { alias: 'plugin.numberinputfilter', extend: 'Ext.AbstractPlugin', constructor : function(cfg) { cfg = cfg || {}; Ext.applyIf(cfg, { allowedDecimalSeparators : ',.' }); Ext.apply(this, cfg); }, init : function(field) { if (!(field && field.isXType('numberfield'))) { return; } Ext.apply(field, { allowedDecimalSeparators : this.allowedDecimalSeparators, checkValue : function(newChar) { var raw = this.getRawValue(); var el = this.inputEl.dom; // http://javascript.nwbox.com/cursor_position/ // cursor.js var start = getSelectionStart(el); var end = getSelectionEnd(el); if (start != end) { // raw = raw.substring(0, start) + raw.substring(end); } if (Ext.isEmpty(raw)) { return (newChar == this.decimalSeparator || (this.minValue < 0) && newChar == '-') || /^\d$/.test(newChar); } if (raw.length == this.maxLength) { return false; } if (newChar == this.decimalSeparator && (!this.allowDecimals || raw.indexOf(this.decimalSeparator) != -1)) { return false; } // raw = raw.substring(0, start) + newChar + raw.substring(start); raw = raw.split(new RegExp(Ext.String.escapeRegex(this.decimalSeparator))); return (!raw[0] || this.intRe.test(raw[0])) && (!raw[1] || this.decRe.test(raw[1])); }, filterKeys : function(e){ if (e.ctrlKey && !e.altKey) { return; } var key = e.getKey(), charCode = String.fromCharCode(e.getCharCode()); if(Ext.isGecko && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){ return; } if(!Ext.isGecko && e.isSpecialKey() && !charCode){ return; } // begin hack if (charCode != this.decimalSeparator && this.allowedDecimalSeparators.indexOf(charCode) != -1) { // , // , // charCode = this.decimalSeparator; if (Ext.isIE) { // IE e.browserEvent.keyCode = charCode.charCodeAt(0); } else if (Ext.isGecko) { // gecko- e.stopEvent(); // var newEvent = document.createEvent('KeyEvents'); // , // .. , // newEvent.initKeyEvent( e.browserEvent.type, e.browserEvent.bubbles, true, //cancellable e.browserEvent.view, e.browserEvent.ctrlKey, e.browserEvent.altKey, e.browserEvent.shiftKey, e.browserEvent.metaKey, 0, // keyCode charCode.charCodeAt(0) // charCode ); e.getTarget().dispatchEvent(newEvent); // , . return; } else if (Ext.isWebKit) { // e.stopEvent(); // webkit initKeyboardEvent , TextEvent if (this.checkValue(charCode)) { var newEvent = document.createEvent('TextEvent'); newEvent.initTextEvent( 'textInput', e.browserEvent.bubbles, true, e.browserEvent.view, charCode ); e.getTarget().dispatchEvent(newEvent); } return; } } if (!this.checkValue(charCode)) { e.stopEvent(); } // end hack }, updateDecimalPrecision : function(prec, force) { if (prec == this.decimalPrecision && force !== true) { return; } if (!Ext.isNumber(prec) || prec < 1) { this.allowDecimals = false; } else { this.decimalPrecision = prec; } var intRe = '^'; if (this.minValue < 0) { intRe += '-?'; } intRe += '\\d' + (Ext.isNumber(this.integerPrecision) ? '{1,' + this.integerPrecision + '}' : '+') + '$'; this.intRe = new RegExp(intRe); if (this.allowDecimals) { this.decRe = new RegExp('^\\d{1,' + this.decimalPrecision + '}$'); } else { delete this.decRe; } }, fixPrecision : function(value) { // support decimalSeparators if (Ext.isString(value)) { value = value.replace(new RegExp('[' + Ext.String.escapeRegex(this.allowedDecimalSeparators + this.decimalSeparator) + ']'), '.'); } // end hack var me = this, nan = isNaN(value), precision = me.decimalPrecision; if (nan || !value) { return nan ? '' : value; } else if (!me.allowDecimals || precision <= 0) { precision = 0; } return parseFloat(Ext.Number.toFixed(parseFloat(value), precision)); } }); field.updateDecimalPrecision(field.decimalPrecision, true); } }); Ext.onReady(function() { Ext.create('Ext.window.Window', { renderTo : Ext.getBody(), width : 300, height : 230, minWidth : 300, minHeight : 230, closable : false, bodyStyle : 'padding:5px', layout : 'border', title : 'NumberInputFilterPlugin - Demo', items : [{ region : 'north', xtype : 'fieldset', defaults : { xtype : 'numberfield', hideTrigger : true, msgTarget : 'side', autoFitErrors : true }, title : 'without plugin', items : [{ fieldLabel : 'simple' },{ fieldLabel : 'autoStripChars', autoStripChars : true }] },{ region : 'center', xtype : 'fieldset', title : 'with plugin', defaults : { xtype : 'numberfield', hideTrigger : true, msgTarget : 'side', autoFitErrors : true }, layout : 'anchor', items : [{ fieldLabel : 'non negative', minValue : 0, plugins : Ext.create('plugin.numberinputfilter') },{ fieldLabel : '"@,./#" as decimal separators', plugins : Ext.create('plugin.numberinputfilter', { allowedDecimalSeparators : '@,./#' }) }] }] }).show(); Ext.tip.QuickTipManager.init(); });
UPD:firefoxでフィルタリングを機能させる方法> 12.実装は
ここから 、firefoxのバージョンをどのように決定するか、誰にも見せる必要はないと思います。 これに問題がある場合は、こちらをご覧
ください 。 したがって、filterKeysのコードは次のとおりです。
... } else if (Ext.isGecko) { // gecko- e.stopEvent(); // https://bugzilla.mozilla.org/show_bug.cgi?id=749185 // firefoxVersion . if (firefoxVersion < 12) { // var newEvent = document.createEvent('KeyEvents'); // , // .. , // newEvent.initKeyEvent( e.browserEvent.type, e.browserEvent.bubbles, true, //cancellable e.browserEvent.view, e.browserEvent.ctrlKey, e.browserEvent.altKey, e.browserEvent.shiftKey, e.browserEvent.metaKey, 0, // keyCode charCode.charCodeAt(0) // charCode ); e.getTarget().dispatchEvent(newEvent); } else if (this.checkValue(charCode)) { // http://forums.mozillazine.org/viewtopic.php?p=12198605&sid=3723622be9117f663d16d522fe03deb5#p12198605 var tgt = e.getTarget(); if ('selectionStart' in tgt) { if (tgt.selectionStart == tgt.textLength) { tgt.value += charCode; } else { var lastpos = tgt.selectionStart; tgt.value = tgt.value.substr(0, lastpos) + charCode + tgt.value.substr(lastpos); tgt.selectionStart = lastpos + 1; tgt.selectionEnd = lastpos + 1; } } } return; } else if (Ext.isWebKit) { ...