EXT 4.2 使用 XTemplate 对结果进行 ComboBox 分组

问题描述 投票:0回答:6

我正在尝试对从商店获得的结果进行分组,以显示在 ComboBox 内。 我有一个如下所示的组合框: enter image description here

我需要它看起来像这样:

enter image description here

这意味着按类别(订单/发票)分组。

我的组合框定义如下:

Ext.define('NG.view.searchcombo.Combo', {
    requires: ['Ext.form.field.ComboBox'],
    extend: 'Ext.form.ComboBox',
    alias: 'widget.searchcombo',
    minChars:3,
    fieldLabel: 'Choose Search',
    store: 'Search',
    displayField: 'name',
    valueField: 'id',
    typeAhead: false,
    hideLabel: true,
    hideTrigger:false,
    anchor: '100%',

    listConfig: {
        loadingText: 'Searching...',
        emptyText: 'No matching posts found.',

        // Custom rendering template for each item
        getInnerTpl: function() {
            return '<h3>{name} / {category}</h3>' +'{excerpt}' ;
        }
    },
    pageSize: 10,
    initComponent: function () {    

        this.callParent(arguments);
    }
});

我的数据是这样的:

[{
    "id": 1,
    "name": "one",
    "category": "invoice"
}, {
    "id": 2,
    "name": "two",
    "category": "invoice"
}, {
    "id": 3,
    "name": "one",
    "category": "order"
}, {
    "id": 4,
    "name": "two",
    "category": "order"
}, {
    "id": 5,
    "name": "three",
    "category": "invoice"
}, {
    "id": 6,
    "name": "four",
    "category": "invoice"
}, {
    "id": 7,
    "name": "three",
    "category": "order"
}, {
    "id": 8,
    "name": "four",
    "category": "order"
}, {
    "id": 9,
    "name": "five",
    "category": "invoice"
}, {
    "id": 10,
    "name": "six",
    "category": "invoice"
}, {
    "id": 11,
    "name": "five",
    "category": "order"
}, {
    "id": 12,
    "name": "six",
    "category": "order"
}, {
    "id": 13,
    "name": "seven",
    "category": "invoice"
}, {
    "id": 14,
    "name": "eight",
    "category": "invoice"
}, {
    "id": 15,
    "name": "seven",
    "category": "order"
}, {
    "id": 16,
    "name": "eight",
    "category": "order"
}]

我认为可以通过使用

Ext.XTemplate
来完成,但我不熟悉
Ext.XTemplate

javascript extjs combobox grouping extjs4.2
6个回答
14
投票

我想要一个更简单的解决方案,所以我将分享我的想法。

出于我的目的,我有一个

key
,我想对其进行分组,它是单个字符。我知道我想要为每个键显示的标题,因此我对列表进行了预先排序以确保类型组合在一起,然后每次看到新键时我都会渲染一个组标题。

myStore.sort('key', 'DESC');

Ext.create('Ext.form.field.ComboBox', {
  store: myStore,
  queryMode: 'local',
  displayField: 'name',
  valueField: 'id',
  listConfig: {
    cls: 'grouped-list'
  },
  tpl: Ext.create('Ext.XTemplate',
    '{[this.currentKey = null]}' +
    '<tpl for=".">',
      '<tpl if="this.shouldShowHeader(key)">' +
        '<div class="group-header">{[this.showHeader(values.key)]}</div>' +
      '</tpl>' +
      '<div class="x-boundlist-item">{name}</div>',
    '</tpl>',
    {
      shouldShowHeader: function(key){
        return this.currentKey != key;
      },
      showHeader: function(key){
        this.currentKey = key;
        switch (key) {
          case 's': return 'Structures';
          case 'f': return 'Filters';
          ...
        }
        return 'Other';
      }
    }
  )
});

使用以下CSS:

.grouped-list .x-boundlist-item {
  padding: 1px 3px 0 10px
}

.grouped-list .group-header {
  padding: 4px;
  font-weight: bold;
  border-bottom: 1px solid #ddd;
}

这个数据:

[
    { key: 's', name: '2014 Product Development' },
    { key: 'f', name: 'Message Filter' },
    { key: 's', name: '2014 Product Development (Little)' },
    { key: 's', name: 'Global Structure' },
    { key: 'f', name: 'My SW' }
]

我得到了一个看起来很漂亮的分组列表,如下所示:


8
投票

这是一个扩展,通过从 Sean Adkinson 的代码中创建一个可重用的组件来改进上面的答案。

我用带有 Ext 5.0.1 的 GridPanel 替换 BoundList 得到了不同的结果,因为这就是我使用的。

需要注意的是,它不支持折叠组,但它对我来说非常有用。

在 Extjs 4.2.3 和 5.0.1 中测试。

你可以在 Sencha fiddle

中看到它

希望它能帮助那里的人。

Ext.define('Ext.ux.GroupComboBox', {
  extend: 'Ext.form.field.ComboBox',
  alias: 'widget.groupcombobox',
  /*
   * @cfg groupField String value of field to groupBy, set this to any field in your model
   */
  groupField: 'group',
  listConfig: {
    cls: 'grouped-list'
  },
  initComponent: function() {
    var me = this;
    me.tpl = new Ext.XTemplate([
      '{%this.currentGroup = null%}',
      '<tpl for=".">',
      '   <tpl if="this.shouldShowHeader(' + me.groupField + ')">',
      '       <div class="group-header">{[this.showHeader(values.' + me.groupField + ')]}</div>',
      '   </tpl>',
      '   <div class="x-boundlist-item">{' + me.displayField + '}</div>',
      '</tpl>', {
        shouldShowHeader: function(group) {
          return this.currentGroup != group;
        },
        showHeader: function(group) {
          this.currentGroup = group;
          return group;
        }
      }
    ]);
    me.callParent(arguments);
  }
});

//Example usage
var Restaurants = Ext.create('Ext.data.Store', {
  storeId: 'restaraunts',
  fields: ['name', 'cuisine'],
  sorters: ['cuisine', 'name'],
  groupField: 'cuisine',
  data: [{
    name: 'Cheesecake Factory',
    cuisine: 'American'
  }, {
    name: 'University Cafe',
    cuisine: 'American'
  }, {
    name: 'Creamery',
    cuisine: 'American'
  }, {
    name: 'Old Pro',
    cuisine: 'American'
  }, {
    name: 'Nola\'s',
    cuisine: 'Cajun'
  }, {
    name: 'House of Bagels',
    cuisine: 'Bagels'
  }, {
    name: 'The Prolific Oven',
    cuisine: 'Sandwiches'
  }, {
    name: 'La Strada',
    cuisine: 'Italian'
  }, {
    name: 'Buca di Beppo',
    cuisine: 'Italian'
  }, {
    name: 'Pasta?',
    cuisine: 'Italian'
  }, {
    name: 'Madame Tam',
    cuisine: 'Asian'
  }, {
    name: 'Sprout Cafe',
    cuisine: 'Salad'
  }, {
    name: 'Pluto\'s',
    cuisine: 'Salad'
  }, {
    name: 'Junoon',
    cuisine: 'Indian'
  }, {
    name: 'Bistro Maxine',
    cuisine: 'French'
  }, {
    name: 'Three Seasons',
    cuisine: 'Vietnamese'
  }, {
    name: 'Sancho\'s Taquira',
    cuisine: 'Mexican'
  }, {
    name: 'Reposado',
    cuisine: 'Mexican'
  }, {
    name: 'Siam Royal',
    cuisine: 'Thai'
  }, {
    name: 'Krung Siam',
    cuisine: 'Thai'
  }, {
    name: 'Thaiphoon',
    cuisine: 'Thai'
  }, {
    name: 'Tamarine',
    cuisine: 'Vietnamese'
  }, {
    name: 'Joya',
    cuisine: 'Tapas'
  }, {
    name: 'Jing Jing',
    cuisine: 'Chinese'
  }, {
    name: 'Patxi\'s Pizza',
    cuisine: 'Pizza'
  }, {
    name: 'Evvia Estiatorio',
    cuisine: 'Mediterranean'
  }, {
    name: 'Gyros-Gyros',
    cuisine: 'Mediterranean'
  }, {
    name: 'Mango Caribbean Cafe',
    cuisine: 'Caribbean'
  }, {
    name: 'Coconuts Caribbean Restaurant &amp; Bar',
    cuisine: 'Caribbean'
  }, {
    name: 'Rose &amp; Crown',
    cuisine: 'English'
  }, {
    name: 'Baklava',
    cuisine: 'Mediterranean'
  }, {
    name: 'Mandarin Gourmet',
    cuisine: 'Chinese'
  }, {
    name: 'Bangkok Cuisine',
    cuisine: 'Thai'
  }, {
    name: 'Darbar Indian Cuisine',
    cuisine: 'Indian'
  }, {
    name: 'Mantra',
    cuisine: 'Indian'
  }, {
    name: 'Janta',
    cuisine: 'Indian'
  }, {
    name: 'Starbucks',
    cuisine: 'Coffee'
  }, {
    name: 'Peet\'s Coffee',
    cuisine: 'Coffee'
  }, {
    name: 'Coupa Cafe',
    cuisine: 'Coffee'
  }, {
    name: 'Lytton Coffee Company',
    cuisine: 'Coffee'
  }, {
    name: 'Il Fornaio',
    cuisine: 'Italian'
  }, {
    name: 'Lavanda',
    cuisine: 'Mediterranean'
  }, {
    name: 'MacArthur Park',
    cuisine: 'American'
  }, {
    name: 'St Michael\'s Alley',
    cuisine: 'Californian'
  }, {
    name: 'Cafe Renzo',
    cuisine: 'Italian'
  }, {
    name: 'Miyake',
    cuisine: 'Sushi'
  }, {
    name: 'Sushi Tomo',
    cuisine: 'Sushi'
  }, {
    name: 'Kanpai',
    cuisine: 'Sushi'
  }, {
    name: 'Pizza My Heart',
    cuisine: 'Pizza'
  }, {
    name: 'New York Pizza',
    cuisine: 'Pizza'
  }, {
    name: 'Loving Hut',
    cuisine: 'Vegan'
  }, {
    name: 'Garden Fresh',
    cuisine: 'Vegan'
  }, {
    name: 'Cafe Epi',
    cuisine: 'French'
  }, {
    name: 'Tai Pan',
    cuisine: 'Chinese'
  }]
});

Ext.create('Ext.container.Viewport', {
  items: Ext.create('Ext.ux.GroupComboBox', {
    fieldLabel: 'Restaurants',
    name: 'txtRestaurant',
    forceSelection: true,
    editable: false,
    queryMode: 'local',
    triggerAction: 'all',
    multiSelect: true,
    groupField: 'cuisine',
    displayField: 'name',
    valueField: 'name',
    store: Restaurants,
    width: 400
  })
}).show();
.grouped-list .x-boundlist-item {
  padding: 1px 3px 0 10px;
}
.grouped-list .group-header {
  padding: 4px;
  font-weight: bold;
  border-bottom: 1px solid #ddd;
}


2
投票

请,您可以使用网格来渲染组合框内容来完成此操作。参考这篇文章:http://www.sencha.com/forum/showthread.php?132328-CLOSED-ComboBox-using-Grid-instead-of-BoundList

根据这篇文章,我能够创建这个:

enter image description here


0
投票

我已经实现了我自己的组合版本,其中网格作为其列表组件。您可以在 GitHub 上获取它,我已经在网上放了一些示例

第三个示例与您想要实现的目标非常匹配。

这是一个更加匹配的示例。您只需要做造型即可:

Ext.widget('gridpicker', {

    queryMode: 'local'
    ,displayField: 'name'

    ,store: {
        fields: ['name', 'group']
        ,proxy: {type: 'memory', reader: 'array'}
        ,data: ...
        ,groupField: 'group'
        ,sorters: {property: 'name', order: 'ASC'}
    }

    ,gridConfig: {
        features: [{
            ftype:'grouping'
            ,groupHeaderTpl: '{name}'
            ,collapsible: false
        }]
        ,columns: [{
            width: 30
            ,renderer: function(value, md, record, rowIndex) {
                return '<img src="..." />';
            }
        },{
            dataIndex: 'name'
            ,flex: 1
        }]
    }
});

0
投票

这是为我工作的代码:

如果您使用 Sencha Architect,请在 Override 中添加 createPicker 并手动将 listConfig 创建为对象。

{
    xtype: 'combobox',
    createPicker: function() {
        var me = this,
            picker,
            menuCls = Ext.baseCSSPrefix + 'menu',
            opts = Ext.apply({
                selModel: {
                    mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
                },
                floating: true,
                hidden: true,
                ownerCt: me.ownerCt,
                cls: me.el.up('.' + menuCls) ? menuCls : '',
                store: me.store,
                displayField: me.displayField,
                focusOnToFront: false,
                pageSize: me.pageSize
            }, me.listConfig, me.defaultListConfig);

        // NOTE: we simply use a grid panel
        //picker = me.picker = Ext.create('Ext.view.BoundList', opts);


        picker = me.picker = Ext.create('Ext.grid.Panel', opts);

        // hack: pass getNode() to the view
        picker.getNode = function() {
            picker.getView().getNode(arguments);
        };

        me.mon(picker.getView(), {
            refresh: me.onListRefresh,
            scope: me
        });
        me.mon(picker, {
            itemclick: me.onItemClick,
            //            refresh: me.onListRefresh,
            scope: me
        });

        me.mon(picker.getSelectionModel(), {
            selectionChange: me.onListSelectionChange,
            scope: me
        });

        return picker;
    },
    listConfig: {
        columns: [{
            xtype: "gridcolumn",
            dataIndex: "id",
            text: "Id"
        }, {
            xtype: "gridcolumn",
            dataIndex: "name",
            text: "Name"
        }],
        features: [{
            ftype: "grouping"
        }]
    },
    fieldLabel: 'Label',
    queryMode: 'local',
    store: 'myTestStore'
}

0
投票

Sean Adkinson 的答案已改编并适用于 Ext Js 7.7.0。

参见小提琴

代码:

Ext.application({
    name: 'Fiddle',

    launch: function () {
        //Ext.Msg.alert('Fiddle', 'Welcome to Sencha Fiddle!');
        Ext.create('Ext.form.Panel', {
            renderTo: Ext.getBody(),
            items: [{
                xtype: 'combo',
                fieldLabel: 'SomeCombo',
                valueField: 'id',
                displayField: 'desc',
                tpl: Ext.create('Ext.XTemplate',
                    // `<ul class="x-list-plain">
                    // <tpl for=".">
                    // <li role="option" class="x-boundlist-item">{id} - {desc}</li>
                    // </tpl></ul>
                    // `

                    `{[this.currentKey = null]}
                        <tpl for=".">
                          <tpl if="this.shouldShowHeader(gf)">
                            <div style="font-weight: bold;">{[this.showHeader(values)]}</div>
                          </tpl>
                          <div class="x-boundlist-item">{desc}</div>
                        </tpl>`, {
                        shouldShowHeader: function (key) {
                            return this.currentKey != key;
                        },
                        showHeader: function (values) {
                            this.currentKey = values.gf;
                            return values.gf;
                        }
                    }
                ),
                store: {
                    fields: [{
                        name: 'id',
                        type: 'int'
                    }, {
                        name: 'gf',
                        type: 'string'
                    }, {
                        name: 'desc',
                        type: 'string'
                    }],
                    data: [{
                        id: 1,
                        gf: 'Group 1',
                        desc: 'Item 1 - 1'
                    }, {
                        id: 2,
                        gf: 'Group 1',
                        desc: 'Item 1 - 2'
                    }, {
                        id: 3,
                        gf: 'Group 2',
                        desc: 'Item 2 - 1'
                    }, {
                        id: 4,
                        gf: 'Group 2',
                        desc: 'Item 2 - 2'
                    }]
                }
            }]
        });
    }
});


© www.soinside.com 2019 - 2024. All rights reserved.