2

我想在组合框中为在数据库中没有条目的记录(这是一个历史数据,存储在数据库中并加载到表单上)设置一个显示字段。

例如,我在商店中有 3 个条目,如下所示 - 值:显示字段 a:苹果 b:球 c:猫

像往常一样,它用于预填充组合框(在这里工作正常)。

我有存储在数据库中的历史数据,其中包含一些 id 为 d 的记录的值,这些记录在商店中不再存在,但我仍然想支持旧数据并希望显示关联的显示字段(“娃娃”)它。

我的查询是,即使此记录不在与组合框关联的商店中,如何在从数据库中获取记录时设置显示字段?目前,我们在 extJs 中没有诸如 setDisplayField 之类的方法,但是我们有一个方法 'setValue' 可以更改组合框中的值,这是不希望的。

我想在从数据库中遇到 'id =d' 时将显示字段更改为 'doll'。

请帮忙。

谢谢

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方法。此方法返回的任何记录都将用于更新组合框显示及其值,与此记录是否存在于组合存储中无关,并且不会影响可以在组合中选择的值。完美的!

假设我们在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调用时没有加载legacy store怎么办?那是行不通的。在其 vanilla 形式中,如果未加载其存储,组合框将中止其setValue方法,并从其方法中回调它onLoad。我们应该添加一些代码来为我们的旧商店复制这种行为。

第二个问题比较琐碎,但是如果使用历史值初始化组合,并且用户更改组合的值但随后后悔他们的选择怎么办?如果不取消并重新填写整个表单,他们将无法取回原始值。为了避免他们感到沮丧,我们可以更改组合行为以在清除时恢复其原始值,而不是其最后一个值。由于我不知道这是否可取,因此我们将其添加为选项。

考虑到这一切,这里有一个“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 回答
0

您只需要在使用加载后将该记录添加到商店store.add({id:'d',name:'doll'})

这是一个不是您需要但显示概念的示例:

Ext.define('MyApp.store.Users', {
    extend:'Ext.data.Store',
    model:'MyApp.model.User',
    listeners:{
        load:function (store, recs) {
            store.add({uid:'', name:''});  //adding empty record to enable deselection of assignment
        }
    }
});
于 2013-09-27T23:50:59.727 回答