Extjs sorting store by alphanumeric field - javascript

I've got a bunch of records whose names are like "Itemtype #", and when they are outputted into a tree view, they're sorted incorrectly, like so:
Item 1
Item 10
Item 11
Item 12
Item 13
Item 2
Item 3
Item 4
My model fields are defined as follows, and I am sorting on "Name":
fields: [
{ name: 'Id', defaultValue: 0, type: 'int', mapping: 'Id' },
{ name: 'Name', defaultValue: '', type: 'string', mapping: 'Name', sortType: Ext.data.SortTypes.asUCString },
{ name: 'Type', defaultValue: 0, type: 'int', mapping: 'Type' },
{ name: 'CreationDate', type: 'date', mapping: 'CreationDate' }
],
Can anybody point me in the right direction? I'm using extjs 4.0.5

In store you have to set up how the data will display by sorters property:
var store = Ext.create('Ext.data.JsonStore', {
.
.
.
remoteSort: false, //true for server sorting
sorters: [{
property: 'Name',
direction: 'DESC' // or 'ASC'
}],
.
.
.
})

Try calling TreeStore.sort with a sorter config with a sorting callback. A simple field sort won't do in this case because the naive alpha sort isn't what you want. The example at the top of the Ext.util.Sorter doc shows how to do it in a store, but you can just as easily add it to the sorters param of your model.

Related

How to bind dynamic select options for each row in jsGrid?

i try to bind select option dynamically from database,
for example:
$("#grid").jsGrid({
editing: true,
autoload: true,
paging: false,
pageLoading: true,
data: result,
fields: [
{ name: "Units", type: "select", title: "Units", items: ? },
]
});
but What's the data format to create select with options.
i try "itemTemplate" but didn't work well.
Build your select list before you instantiate the grid. Example:
const units = [ { id: 0, name: "cm"}, { id: 1, name: "inch" } ];
$("#grid").jsGrid({
// ...
fields: [
{ name: "Units", type: "select", title: "Units",
items: units, valueField: "id", textField: "name" },
]
});
Working demo at JSFiddle.
If you are talking about different rows having different drop down lists for the same column, then the built-in select type may not be useful. You have to render the editTemplate yourself. A simple working example is in this JSFiddle.

Displaying the query using jquerybuilder

I used jQuery QueryBuilder (http://querybuilder.js.org/) to create the rules and generate JSON and store them in a database. Now, I need to do the reverse operation. This means,that the input will be in the form of a JSON which would get parsed and display the rules in the UI in the same format of QueryBuilder for modification/deletion. Can someone provide pointers/sample code on how to achieve this?
To achive this you have to use the *setRules * method --> see documentation here
var rules_json= {
condition: 'AND',
rules: [{
id: 'price',
operator: 'less',
value: 10.25
}, {
condition: 'OR',
rules: [{
id: 'category',
operator: 'equal',
value: 2
}, {
id: 'category',
operator: 'equal',
value: 1
}]
}]
};
$('#queryBuilder').queryBuilder('setRules', rules_json);

How to display nested objects in a GridPanel in ExtJS (and Sencha architect)?

I've got an incoming array of Offering objects that looks like this:
[{
"id" : 16,
"price" : 500,
"quantity" : 2000,
"denomination" : "case",
"denominationPlural" : "cases",
"product" : {
"id" : 14,
"description" : "This is the text description for product 14."
}
}, {
"id" : 18,
"price" : 500,
"quantity" : 1,
"denomination" : "study",
"denominationPlural" : "studies",
"product" : {
"id" : 17,
"description" : "This is a text description for product 17."
}
}]
An Offering is a Product times a quantity for a price.
Now I want to display these Offerings in a GridPanel, but also include the nested product information in those rows. Here's how I was doing it before:
Ext.define('Offering', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'number'},
{name: 'price', type: 'number'},
{name: 'quantity', type: 'number'},
{name: 'denomination', type: 'string'},
{name: 'denominationPlural', type: 'string'},
{name: 'product.description', type: 'string'},
]
});
var offeringStore = Ext.create('Ext.data.Store', {
autoDestroy: true,
model: 'Offering',
proxy: {
type: 'ajax',
url: '/offerings',
reader: {
type: 'json',
root: 'success'
}
},
autoLoad:true
});
var offeringGrid = Ext.create('Ext.grid.Panel', {
store: offeringStore,
columns: [{
header: 'id',
dataIndex: 'id'
}, {
header: 'price',
dataIndex: 'price'
}, {
header: 'quantity',
dataIndex: 'quantity'
}, {
header: 'denomination',
dataIndex: 'denomination'
}, {
header: 'description',
dataIndex: 'product.description'
},
};
And this worked just fine. Then, somewhere along the way (which included an upgrade to ExtJS 5.1.1 (from ExtJS 4.2.1) and use of Sencha Architect, it broke.
Problem 1: Sencha Architect prevents creating an entry for "product.description" in the Offering Model, complaining about the "." character. But if you create it as "whateveryouwant", you can go into the model field and rename it to "product.description" there.
Problem 2: after working around the "." issue and running the application, the "product.description" column's cells are blank.
Problem 3: the javascript console produces zero errors. The incoming data looks fine.
How should I go about getting this nested data to show up?
The way I do it is quite simple. Just add the mapping attribute to the model this way:
{
name: 'product.description',
type: 'string',
mapping : 'product.description'
}
No need for renderer.
My current workaround, which may help others in the same situation, uses a "renderer" clause to display the information desired.
{
xtype: 'gridpanel',
title: 'Offerings',
bind: {
store: '{OfferingsStore}'
},
columns: [
...
{
xtype: 'gridcolumn',
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
return record.getData().product.description;
},
text: 'Product Description'
},
...
]
But I'd still like an answer about using "product.description" in the Offering Model definition rather than this rendering trick.

Remote Filtering with ListFilter in ExtJS Grid Column Header

I am using ListFilter plugin to filter results on a Grid panel. The column definition is.
{
header: 'Provider',
filter: {
type: 'list',
store: Ext.getStore('MyApp.store.Provider'),
dataIndex: 'provider_id',
labelField: 'name'
}
}
MyApp.store.Provider is created as
Ext.create('Ext.data.Store', {
storeId: 'MyApp.store.Provider',
autoDestroy: true,
autoLoad: {start: 0, limit: 50},
autoSync: true,
model: 'MyApp.model.Provider',
pageSize: 50,
proxy: {
type: 'ajax',
api: {
create: 'proxy/provider/create',
read: 'proxy/provider/read',
update: 'proxy/provider/update',
destroy: 'proxy/provider/destroy'
},
reader: {
type: 'json',
root: 'data',
successProperty: 'success',
messageProperty: 'message',
totalProperty: 'total'
},
writer: {
allowSingle: false,
type: 'json',
writeAllFields: false,
root: 'data'
}
}
});
And lastly model MyApp.model.Provider is defined as
Ext.define('MyApp.model.Provider', {
extend: 'Ext.data.Model',
fields: [
{ name: 'provider_id', type: 'int'},
'name',
{ name: 'create_time', type: 'date', dateFormat: appDateFormat },
{ name: 'status', type: 'int'}
],
idProperty: 'provider_id',
displayProperty: 'name' // A custom property used for dynamically use the property to display
})
Now this code does not show any sub-menu in the filter menu. It just shows loading. See the image.
Update
I have solved it using following filter config. This actually populates options config manually. So no store is used here.
{
type: 'list',
labelField: 'name',
options: (function () {
var opts = [];
fS.load(function (records, operation, success) {
for (var i = 0; i < records.length; i++) {
var ar = {
id: records[i].get('provider_id'),
name: records[i].get('name')
};
opts.push(ar);
}
});
return opts;
})(),
single: true
}
It seems 'id' is hard-coded. id: records[i].get('provider_id'), does not look good. Though it works.
But I am still looking for a proper way to do it.
Note: The expected behavior can be found on ExtJS 4.1.1. See this jsfiddle. I have reproduced it. But this very same thing does not work on ExtJS 4.0.7
I didn't tried this myself but you need to set the ID manually with the idField property [new to ExtJS4.1.3] which is per default set to id. So I guess this will work:
{
header: 'Provider',
filter: {
type: 'list',
idField: 'provider_id',
store: Ext.getStore('MyApp.store.Provider'),
dataIndex: 'provider_id',
labelField: 'name'
}
}
Update
OK, I looked at the source and I can now tell you that this is the answer. So will have to either live with your workarround until 4.2 is out or you can apply the following changes to your Ext.ux.grid.menu.ListMenu to make it run:
add the idField with a default value.
look within the constructor for this lines
case 'object': options.push([value.id, value[this.labelField]]); break;
// some more lines
fields: ['id', this.labelField],
and replace it with
case 'object': options.push([value[me.idField], value[me.labelField]]); break;
// some more lines
fields: [me.idField, me.labelField],
and within the onLoad function look for
itemValue = records[i].get('id');
and replace it with
itemValue = records[i].get(me.idField);
and that pretty much is it.

Displaying values in an ext grid where the values correspond to strings in another table/model

I'm teaching myself ExtJS by building a really simple 'scrum' development tracking application. I'm currently displaying the "Backlog" as a grid panel that displays the properties of the card(user story).
Card.js (Card model)
Ext.define('AM.model.Card', {
extend: 'Ext.data.Model',
fields: [
'id',
'name',
'priority_id',
...
]
});
Priority.js (Priority model)
Ext.define('AM.model.Priority', {
extend: 'Ext.data.Model',
fields: [
'id',
'name',
'short_name'
]
});
So the data for the card will look something like this:
backlogcards.json (data)
{
success: true,
backlogcards: [
{
id: 1,
name: 'ONEs',
priority_id: 2,
...
},
{
id: 2,
name: 'TWOs',
priority_id: 3,
...
}
]
}
And the priorities looks like this:
priorities.json (data)
{
success: true,
priorities: [
{
id : 1,
name : "High",
short_name : "H"
},
{
id : 2,
name : "Medium",
short_name : "M"
},
{
id : 3,
name : "Low",
short_name : "L"
}
]
}
So ideally what I would like to do is have the grid panel display the short_name for the corresponding priority_id. When the item is clicked on to be edited inline, a combo box will be displayed that shows the name property. I'm half way there already.
BacklogList.js (view)
Ext.define('AM.view.card.BacklogList', {
extend: 'Ext.grid.Panel',
alias: 'widget.backlogcardlist',
title: 'Backlog',
store: 'BacklogCards',
selType: 'cellmodel',
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})
],
columns: [
{ header: 'ID', dataIndex: 'id' },
{ header: 'Name', dataIndex: 'name', field: 'textfield' },
{
header: 'Priority',
dataIndex: 'priority_id',
width: 130,
field: {
xtype: 'combobox',
typeAhead: true,
store: 'Priorities',
displayField: 'name',
valueField: 'id',
listClass: 'x-combo-list-small'
}
}
]
});
So I know the 'dataIndex' property is what I need to modify in order to change the display, but I'm not sure how to tie those two stores together.
As you can see above, priority is being displayed as a number instead of the short_name.
Is this a situation where I would need to use associations? (I only know OF them) Sencha Docs
Thank you!
EDIT1: Oh I realize I could 'hard code' a renderer property that does this change, but I would like to avoid that and instead use values from the priorities store.
renderer: function(value){
if (value==3)
{
return "L";
}
else if (value==2)
{
return "M";
}
else
{
return "H";
}
},
EDIT2 for Evan:
Priorities store
Ext.define('AM.store.Priorities', {
extend: 'Ext.data.Store',
model: 'AM.model.Priority',
autoLoad: true,
proxy: {
type: 'ajax',
api: {
read: 'app/data/priorities.json',
update: 'app/data/updateUsers.json'
},
reader: {
type: 'json',
root: 'priorities',
successProperty: 'success'
}
}
});
The store.each refers to this store, right? If so, how do I perform the each operation on it?
I tried changing the declaration line to:
var test = Ext.define('AM.store.Priorities', {
And then tried changing your code to test.each but was unsuccessful.
Thanks again!
You need to use a renderer, however there's nothing stopping you from looping over the values in the priorities store and checking, something like:
renderer: function(value) {
var display = '';
store.each(function(rec){
if (rec.get('id') === value) {
display = rec.get('name');
return false;
}
});
return display;
}

Categories