2

データベースにエントリがないレコード (履歴データであり、データベースに保存され、フォームに読み込まれる) のコンボボックスに表示フィールドを設定したいと考えています。

例として、以下のようにストアに 3 つのエントリがあります - 値: 表示フィールド a: リンゴ b: ボール c: 猫

いつものように、コンボボックスを事前設定するために使用されます (ここまでは正常に動作しています)。

データベースに履歴データが保存されています。これには、id: d のレコードの値があり、ストアにはもう存在しませんが、古いデータをサポートし、関連する表示フィールド (「人形」) を表示したいと考えています。それ。

私のクエリは、このレコードがコンボボックスに関連付けられたストアにない場合でも、データベースからレコードを取得するときに表示フィールドを設定するにはどうすればよいですか? 現在、extJs には setDisplayField などのメソッドはありませんが、コンボボックスの値を変更する「setValue」メソッドはありますが、これは望ましくありません。

データベースから「id = d」に遭遇したときに、表示フィールドを「人形」に変更したいと思います。

助けてください。

ありがとうございました

4

3 に答える 3

2

本当に必要なものがsetDisplayFieldメソッドである場合、実際にはメソッドが存在し、呼び出されsetRawValueます。

ただし、それだけで問題を解決できるかどうかは疑わしいです。確かに、それを使用して正しい表示を得ることができますが、価値が失われます...

var legacyData = [
    {id: 'd', name: "Doll"}
];

var combo = Ext.widget({
    renderTo: Ext.getBody()
    ,xtype: 'combo'
    ,fieldLabel: "Regular combo"
    ,valueField: 'id'
    ,displayField: 'name'
    ,queryMode: 'local'
    ,value: 'd'
    ,store: {
        fields: ['id', 'name']
        ,data: []
        ,autoLoad: true
        ,listeners: {
            load: function() {
                var value = combo.value,
                    valueField = combo.valueField,
                    displayField = combo.displayField,
                    o = Ext.Array.findBy(legacyData, function(data) {
                        return data[valueField] === value;
                    });

                if (o) {
                    combo.setRawValue(o[displayField]);
                }

                // Fail! This will alert 'Doll' instead of 'd'.
                //
                // If you set forceSelection to true, the value will be cleared
                // all together.
                //
                alert(combo.getValue());
            }
        }
    }
});

dbring が示唆するように、履歴レコードをコンボのストアに追加すると、ユーザーに履歴レコードを選択させたい場合にのみ、実際に問題が解決します。私の理解では、そうではありません。そうでなければ、そもそも問題がないからです...

コンボ クラスのコードを調べたところ、必要なことを達成するための最善の方法はメソッドをオーバーライドすることだと思いますfindRecord。このメソッドによって返されるレコードは、コンボ ボックスの表示とその値の両方を更新するために使用されます。このレコードがコンボのストアに存在するかどうかに関係なく、コンボで選択できる値に影響を与えることはありません。完全!

コンボにa を追加したとしましょう。これを行うためにメソッドをlegacyStoreオーバーライドする方法は次のfindRecordとおりです。

findRecord: function(field, value) {
    var ls = this.legacyStore,
        record = this.callParent(arguments);
    if (record) {
        // found in current store
        return record;
    } else if (ls) {
        // try legacy value store
        var idx = ls.findExact(field, value);
        return idx !== -1 ? ls.getAt(idx) : false;
    } else {
        return false;
    }
}

さて、対処すべき問題がさらにいくつか残っています...

findRecordまず、呼び出された時点でレガシー ストアがロードされていない場合はどうなるでしょうか。それはうまくいきません。通常の形式ではsetValue、ストアがロードされていない場合、コンボ ボックスはメソッドを中止し、メソッドからコールバックしますonLoad。従来のストアでこの動作を再現するコードを追加する必要があります。

2 番目の問題はもっと些細なことですが、コンボが履歴値で初期化され、ユーザーがコンボの値を変更したものの、その選択を後悔した場合はどうなるでしょうか。フォーム全体をキャンセルして再入力しないと、元の値を取り戻すことはできません。この欲求不満を解消するために、コンボの動作を変更して、最後の値ではなく、クリア時に元の値を復元することができます。これが望ましいかどうかわからないので、オプションとして追加します。

これらすべてを考慮して、特定のユースケースを処理する「ux」の完全なコードを次に示します。

forceSelectionが に設定されtrue、ストアが で作成された場合、コンボ ボックスの初期値を強制終了するバグのグローバルな修正が含まれていることに注意してくださいautoLoad: true。詳細については、この他の質問を参照してください。または、グローバル修正をスキップして、loading: trueを使用するコンボのすべてのストアとレガシー ストアに追加することもできますforceSelection: true

/**
 * Fixes the bug with autoLoad: true and forceSelection: true.
 * 
 * See: https://stackoverflow.com/a/14440479/1387519
 */
Ext.define('Ext.ux.fix.data.Store.AutoLoadLoadingState', {
    override: 'Ext.data.Store'
    ,constructor: function() {
        this.callParent(arguments);
        if (this.autoLoad) {
            this.loading = true;
        }
    }
});

/**
 * A combo box with support for legacy values.
 *
 * Legacy values will be displayed if their value is assigned to the 
 * combo, but they won't be selectable (nor queryable) by the user.
 */
Ext.define('Ext.ux.form.field.LegacyValueCombo', {
    extend: 'Ext.form.field.ComboBox'

    ,alias: 'widget.legacyvaluecombo'

    ,requires: [
        'Ext.ux.fix.data.Store.AutoLoadLoadingState'
    ]

    /**
     * The store that provides legacy values.
     *
     * @cfg {Ext.data.Store/Object/String}
     */
    ,legacyStore: undefined

    /**
     * If `true` and {@link #forceSelection} is also `true`, the
     * {@link #originalValue original value} of the field will be restored when
     * the field is cleared, instead of the last value.
     *
     * This may be useful considering that if the combo original value is a
     * legacy one, and the user changes this value, they will have no other
     * means to get it back afterward.
     *
     * @cfg {Boolean}
     */
    ,restoreOriginalValue: true

    /**
     * Ensure the legacy store is a store instance before initializing the
     * combo box.
     */
    ,initComponent: function() {

        var legacyStore = this.legacyStore;
        if (legacyStore) {
            this.legacyStore = Ext.data.StoreManager.lookup(legacyStore);
        }

        this.callParent(arguments);
    }

    /**
     * Overridden to return a legacy record if the value is not found in the
     * regular store.
     *
     * This method will only work if both stores have been loaded.
     */
    ,findRecord: function(field, value) {
        var ls = this.legacyStore,
            record = this.callParent(arguments);
        if (record) {
            // found in current store
            return record;
        } else if (ls) {
            // try legacy value store
            var idx = ls.findExact(field, value);
            return idx !== -1 ? ls.getAt(idx) : false;
        } else {
            return false;
        }
    }

    /**
     * This method is overridden to support the case where the legacy store
     * loads after the regular store.
     */
    ,setValue: function(value) {
        var store = this.store,
            legacyStore = this.legacyStore;

        if (legacyStore) {

            // If the legacy store has not been loaded, trigger it.
            if (!legacyStore.lastOptions) {
                if (!legacyStore.loading) {
                    legacyStore.load();
                }
            }

            // Legacy store is loading, we must wait for it to be done before
            // processing the value.
            if (legacyStore.loading) {

                // This is done in the parent setValue method, but we are not
                // sure it will be called in time so we must replicate this 
                // code here.
                this.value = value;
                this.setHiddenValue(value);

                // If the regular store is loading, then this method will be
                // called again when it is done. We will see at this time if we
                // still have to wait for the legacy store. In the other case,
                // we have to simulate a call to onLoad when the legacy store
                // has loaded.
                if (!store.loading) {
                    legacyStore.on({
                        single: true
                        ,scope: this
                        ,load: function(legacyStore, records, success) {
                            this.onLoad(store, store.getRange(), success);
                        }
                    });
                }
            }
            // Legacy store is loaded, that's OK to continue.
            else {
                this.callParent(arguments);

                // Implements restoreOriginalValue.
                if (this.restoreOriginalValue) {
                    this.lastSelection = this.originalValue;
                }
            }

            // setValue must return this
            return this;
        }
        // No legacy store, just defer to the super method.
        else {
            return this.callParent(arguments);
        }
    }
});

最後に、この ux が同期ストアと非同期ストアのすべての組み合わせで機能することを示す多くの例を含むライブ デモforceSelectionがありますsetRawValue

于 2013-09-29T16:19:26.980 に答える
0

Ext.XTemplate を作成し、そのテンプレートをコンボボックスの displayTpl プロパティとして設定することで、これを行うことができると思います。次に、テンプレートに、異なる方法で表示する必要があるレコードをテストするメソッドを含めることができます。

displayTpl 構成は文書化されていませんが、ComboBox のソース コードを見てme.displayTplを検索すると、存在しない場合はデフォルトの displayTpl を作成するだけであることがわかります。

于 2013-09-27T19:54:50.310 に答える