How to perform selectAll operation of checkboxmodel with memory proxy in extjs - extjs

I have a extjs grid with local paging.
In order to achieve local paging, I have provided memory proxy in store.
My grid also contains checkboxmodel.
But my problem is that when I click on selectAll button, only current page's data is selected.
Is there any way that when I click on selectAll button, the data from my proxy store should be selected which has the entire data of all pages.
Please find my grid below.
Thanks in advance.
Ext.create('Ext.data.Store', {
id: 'simpsonsStore',
fields: ['dummyOne', 'dummyTwo'],
pageSize: 50,
proxy: {
type: 'memory',
enablePaging: true
},
data: (function () {
var i,
data = [];
for (i = 0; i < 200; i++) {
var obj = {};
obj.dummyOne = Math.random() * 1000;
obj.dummyTwo = Math.random() * 1000;
data.push(obj);
}
return data;
})()
});
var grid = {
xtype: 'grid',
height: '100%',
title: "Grid Panel",
selModel: {
type: 'checkboxmodel',
},
store: 'simpsonsStore',
columns: [{
"xtype": "numbercolumn",
"dataIndex": "dummyOne",
"text": " dummyOne"
}, {
"xtype": "numbercolumn",
"dataIndex": "dummyTwo",
"text": "dummyTwo"
}],
bbar: {
xtype: 'pagingtoolbar',
displayInfo: true
}
};
Ext.create({
xtype: 'window',
items: [grid],
width: 500,
layout: 'card',
height: 500,
autoShow: true
});

This is intended behaviour. Imagine you have a paged store with 10.000 rows total, showing 10 rows per page. When your users selects all, it is very unlikely that she or he wants to really select 10.000 rows, seeing only 10 rows, 1 of 1000 pages.
If you really want to do something with your entire store, checkbox selection model won't help you. You need to create a copy of your store without pageSize, and loop through it like:
store.each(function (model) {
// do something with model, which is practically a row in the store
});
Here store has to be a store similar to simpsonsStore, but with pageSize: undefined, so that it will contain all records. But you have to think about store size in a real word application, if it is too large, it might lead to performance problems.

You can use the following dirty solution:
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.create('Ext.data.Store', {
id: 'simpsonsStore',
fields: ['dummyOne', 'dummyTwo'],
pageSize: 50,
proxy: {
type: 'memory',
enablePaging: true
},
data: (function () {
var i,
data = [];
for (i = 0; i < 200; i++) {
var obj = {};
obj.dummyOne = Math.random() * 1000;
obj.dummyTwo = Math.random() * 1000;
data.push(obj);
}
return data;
})()
});
var grid = {
xtype: 'grid',
height: '100%',
title: "Grid Panel",
selModel: {
type: 'checkboxmodel',
},
store: 'simpsonsStore',
dockedItems: [{
xtype: 'toolbar',
items: [{
xtype: 'button',
text: "Select All",
handler: function () {
var grid = this.up('grid'),
store = grid.getStore();
var allRecords = store.getProxy().getReader().rawData.reduce((akku, modelData) => {
var pageRecord = store.getById(modelData.id);
if (pageRecord) {
akku.push(pageRecord);
} else {
akku.push(store.createModel(modelData));
}
return akku;
}, []);
grid.getSelectionModel().select(allRecords);
console.log(grid.getSelection());
}
}, {
xtype: 'button',
text: "Console Log Selection",
handler: function () {
console.log(this.up('grid').getSelection());
}
}]
}],
columns: [{
"xtype": "numbercolumn",
"dataIndex": "dummyOne",
"text": " dummyOne"
}, {
"xtype": "numbercolumn",
"dataIndex": "dummyTwo",
"text": "dummyTwo"
}],
bbar: {
xtype: 'pagingtoolbar',
displayInfo: true
}
};
Ext.create({
xtype: 'window',
items: [grid],
width: 500,
layout: 'card',
height: 500,
autoShow: true
});
}
});

Related

How to insert a grid into the grid, on the basis of parent grid data?

I am trying to put a grid inside in the all ready available grid's column on the basis of this parent grid data that if there is the data from the API call then my grid should display the data or if there is no data from the API response then it should not display anything in the parent grid column.
I have inserted the grid in the parent grid via the above code
and below is my handler for to display the data in the child grid
I have tried this via the widget column but via this approach it is displaying
displaying the data only from the last response of API
columns:[{
dataIndex: 'JobCode',
text: 'Job Code',
renderer: 'applyCursor',
flex: 1.5,
rowwidget: {
widget: {
xtype: 'grid',
autoLoad: true,
modal: true,
margin: '30 10 10 10',
listeners: {
afterrender: 'loadData',
},
bind: {
store: '{paymentdetailsstore}',
},
columns: [{
text: 'Payment Type',
dataIndex: 'PaymentType_Name',
flex: 0.5
}, {
text: ' Received Date',
dataIndex: 'Received_DateTime',
flex: 0.5
}, {
text: 'Amount($)',
dataIndex: 'Amount',
flex: 0.5
}]
}
}
},{...},{...},{...}],
loadData: function (a, b, c, d, e) {
debugger;
let me = this,
paymenthistorystore = this.getStore('paymenthistorystore'),
paymentdetailsstore = this.getStore('paymentdetailsstore'),
paymenthistorygrid = me.lookupReference('paymenthistory'),
jobId = paymenthistorystore.getData().items,
grid = a,
id;
console.log(jobId);
Ext.each(jobId, function (items) {
id = items.data.JobId;
Ext.Ajax.request({
url: '/api/jobs/GetPaymentHistory',
method: 'GET',
//async: false,
params: {
JobId: id
},
success: function (response, opts) {
debugger;
//var obj = Ext.decode(response.responseText);
paymentdetailsstore = me.getStore('paymentdetailsstore');
try {
data = Ext.decode(response.responseText).data;
} catch (e) {
data = [];
}
paymentdetailsstore.add(data);
console.log(data);
Ext.Msg.alert('Fiddle', 'Store contains ' + paymentdetailsstore.count() + ' records');
},
scope: paymentdetailsstore,
failure: function (response, opts) {
console.log('server-side failure with status code ' + response.status);
}
});
});
}
I want that in the child grid the data must be display as per according to the API response.
The way I'd do this, is have the details as a field (of array type) inside the job model/record and use the rowViewModel to map inner data to the widget. The way it works, it creates a separate viewmodel instance per row, and so if you define a store inside that rowViewModel, it's going to be created for each job separately. Also from inside the rowViewModel, you can access the record's fields ('{record.<field>}'), and thus you can simply bind the store's data to it.
Ext.define('MyView', {
viewModel: {
stores: {
outterGrid: {
fields: ['name', 'innerGridData'],
data: [{
name: 'Foo',
innerGridData: [{
innerField: 'Foo_a'
}, {
innerField: 'Foo_b'
}]
}, {
name: 'Bar',
innerGridData: [{
innerField: 'Bar_a'
}, {
innerField: 'Bar_b'
}]
}]
}
}
},
extend: 'Ext.grid.Panel',
xtype: 'MyView',
bind: {
store: '{outterGrid}'
},
columns:[{
dataIndex: 'name',
flex: 1
}, {
xtype: 'widgetcolumn',
flex: 1,
widget: {
xtype: 'grid',
bind: {
store: '{innerStore}'
},
columns: [{
dataIndex: 'innerField',
flex: 1
}]
}
}],
rowViewModel: {
stores: {
innerStore: {
fields: ['innerField'],
data: '{record.innerGridData}'
}
}
}
});
Ext.application({
name : 'Fiddle',
launch : function() {
Ext.create({
xtype: 'MyView',
width: 300,
height: 300,
renderTo: Ext.getBody()
});
}
});

Form with textfield and grid: send all values to the server

In create and update forms, it is sometimes necessary to give the user the ability to dynamically add fields to values of the same type (more than one phone, more than one address, etc.).
I'm exploring several possibilities to do this.
One of them is to use a grid as a form field.
However, I have doubts about how best to implement this idea, especially on how to send all the form field values (textfield and grid) to the server (and then how to load them later in the form to edit).
Fiddles with some ideas:
One with cellediting plugin https://fiddle.sencha.com/#view/editor&fiddle/2ftp
Another one with roweditin gplugin a https://fiddle.sencha.com/#view/editor&fiddle/2fto
Not sure about the "best to implement", but I have seen so many requirements for multivalue input, that for reusability I have in my toolbox a gridfield similar to the following one:
Ext.define('Ext.ux.GridField', {
extend: 'Ext.form.FieldContainer',
alias: 'widget.gridfield',
initComponent: function () {
var me = this;
if(!me.columns) me.columns = {
dataIndex: 'field1'
};
if(!me.mapFn) me.mapFn = function(value) {
if(Ext.isObject(value)) return value;
return {
field1: value
};
};
if(!me.unmapFn) me.unmapFn = function(record) {
return record.get('field1');
};
me.grid = Ext.widget(Ext.apply({
xtype: 'grid',
viewConfig: {
markDirty: false
},
store: me.store || Ext.create('Ext.data.Store', {
fields: me.columns.map(function(column) {
return {
name: column.dataIndex,
type: column.dataType || 'auto',
defaultValue: column.defaultValue
};
}),
listeners: {
update: me.updateValue,
datachanged: me.updateValue,
scope: me
}
}),
columns: [{
xtype: 'actioncolumn',
getClass: function () {
return 'x-fa fa-times'
},
handler: function(grid, rowIndex, colIndex, item, e, record) {
grid.getStore().remove(record);
},
width: 35
}].concat(me.columns),
bbar: [{
xtype: 'button',
iconCls: 'x-fa fa-pencil',
text: 'Add',
handler: function(btn) {
var grid = btn.up('grid'),
store = grid.getStore(),
record = store.add(Ext.clone(me.emptyRecord) || {})[0];
grid.getPlugin('editor').startEditByPosition({
row: store.indexOf(record),
column: 1
});
}
}],
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
pluginId: 'editor',
clicksToEdit: 1
})
]
}, me.gridConfig)); // "gridConfig" config can override everything on each instance.
me.hiddenField = Ext.widget({
xtype: 'hiddenfield',
name: me.name,
value: '',
allowNull: false,
rawToValue: function (raw) {
return raw;
},
valueToRaw: function (value) {
return value;
},
getRawValue: function () {
return Ext.valueFrom(this.rawValue, '')
},
isEqual: function (a, b) {
return Ext.encode(a) == Ext.encode(b)
},
listeners: {
change: function(field, nV, oV) {
if(!Ext.isArray(nV)) nV = [nV];
var store = me.grid.getStore();
store.removeAll();
store.add(nV.map(me.mapFn));
}
}
});
Ext.apply(me, {
layout: 'fit',
items: [{
xtype:'container',
border: 1,
style: {
borderColor: '#d0d0d0',
borderStyle: 'solid'
},
items: [me.grid]
}, me.hiddenField]
});
me.callParent(arguments);
},
updateValue: function() {
var me = this,
grid = me.grid,
hiddenField = me.hiddenField,
nV = grid.getStore().getRange().map(me.unmapFn, me),
oV = me.hiddenField.getValue();
if(!oV || Ext.isArray(oV) && Ext.encode(nV) != Ext.encode(oV)) {
hiddenField.suspendCheckChange++;
hiddenField.setValue(nV);
hiddenField.suspendCheckChange--;
me.fireEvent('change', me, nV, oV);
}
}
});
which can then be used like this:
},{
xtype: 'gridfield',
fieldLabel: 'Contacts',
name: 'contacts',
columns: [{
text: 'Type',
dataIndex: 'type',
editor:{
xtype: 'combobox',
name: 'type',
valueField: 'name',
displayField: 'name',
store: combostore,
queryMode: 'local'
},
flex: 0.7
},{
text: 'Description',
dataIndex: 'description',
editor:{
xtype: 'textfield',
name: 'description'
},
flex: 1
}],
mapFn: function(value) {
return value;
},
unmapFn: function(record) {
return record.getData();
}
}, {
I have made a fiddle for you based on your fiddle, including working load and save operations on the form, but in ExtJS 6.x. And I have checked that it works with ExtJS 5 as well, although you have to add working icons.

extjs proper way to replace main center panel

In ExtJS, on a menu toolbar button, I am trying to remove the current panel in my center window, then recreate it with the new selection. I do not understand the proper way to do this. So far when I click the menu item, it removes whatever is currently there successfully, then it will add the new panel successfully. The problem is the 2nd time I hit the button I get the following error:
REGISTERING DUPLICATE COMPONENT ID 'mainportalID'.
I realize its telling me I already used this ID, but then what would be the correct way to remove the current panel, and replace with a new one?
Here is my view controller:
selectMenuButton: function (button, e) {
console.log('select menu button section was hit')
console.log(button);
console.log(e);
var optionString = button.text;
var myDetailsPanel = Ext.getCmp('navview');
console.log(myDetailsPanel);
var count = myDetailsPanel.items.items.length;
if (count > 0) {
myDetailsPanel.items.each(function (item, index, len) {
myDetailsPanel.remove(item, false);
});
}
myDetailsPanel.add({
xtype: optionString
});
}
var myStore = Ext.create('ExtApplication1.store.PositionsStore');
var gridSummary = Ext.create('Ext.grid.Panel', {
store: myStore,
width: 600,
title: 'my first grid',
columns: [
{
text: 'AcctNum',
dataIndex: 'AcctNum',
width: 100
},
{
text: 'AcctShortCode',
dataIndex: 'AcctShortCode',
flex: 1
},
{
text: 'Exchange',
dataIndex: 'Exchange',
width: 200
}
]
});
This is my view
Ext.define('ExtApplication1.view.main.MainPortal', {
extend: 'Ext.panel.Panel',
xtype: 'mainportal',
alias: 'widget.mainportal',
id: 'mainportalID',
html: 'user... this is the main portal window',
autoScroll: true,
bodyPadding: 10,
items: [
gridSummary
]
});
adjusted panel
Ext.define('ExtApplication1.view.main.MainPortal', {
extend: 'Ext.panel.Panel',
xtype: 'mainportal',
alias: 'widget.mainportalAlias',
reference: 'gridtextfield',
//id: 'mainportalID',
html: 'user... this is the main portal window',
autoScroll: true,
bodyPadding: 10,
items: [
gridSummary
]
});
adjusted view controller
onComboboxSelect: function (combo, record, eOpts) {
console.log('new listener was hit');
//return selected Item
var selectedValue = record.get('ClientName');
var selectedCID = record.get('ClientID');
//find the grid that was created
var me = this;
console.log(me);
var xxx = this.lookupReference('gridtextfield');
debugger;
//debugger;
var mainPortalView = Ext.getCmp('mainportalID');
var targetGrid = mainPortalView.down('grid');
//find the store associated with that grid
var targetStore = targetGrid.getStore();
//load store
targetStore.load({
params: {
user: 'stephen',
pw: 'forero',
cid: selectedCID
}
//callback: function (records) {
// Ext.each(records, function (record) {
// console.log(record);
// });
// console.log(targetStore);
//}
});
},
added listeners to MainPortal.js
var myStore = Ext.create('ExtApplication1.store.PositionsStore');
var gridSummary = Ext.create('Ext.grid.Panel', {
store: myStore,
width: 600,
title: 'my first grid',
columns: [
{
text: 'AcctNum',
dataIndex: 'AcctNum',
width: 100
},
{
text: 'AcctShortCode',
dataIndex: 'AcctShortCode',
flex: 1
},
{
text: 'Exchange',
dataIndex: 'Exchange',
width: 200
}
],
listeners: {
destroy: function () {
debugger;
}
}
});
Ext.define('ExtApplication1.view.main.MainPortal', {
extend: 'Ext.panel.Panel',
xtype: 'mainportal',
alias: 'widget.mainportalAlias',
//id: 'mainportalID',
itemId: 'mainportalID',
html: 'user... this is the main portal window',
autoScroll: true,
bodyPadding: 10,
items: [
gridSummary
],
listeners: {
destroy: function () {
debugger;
}
}
});

ExtJS 5 - Keep scroll position of grid when datastore reloads

Can anyone tell me how I can keep the position of my scroll bar when my datastore reloads? Below is the code I have for the window/grid and refresh code. Everytime refreshActions executes the scroll bar scrolls to the top of the grid:
preserveScrollonRefresh does not work in this scenario
View
Ext.define('Tool.view.ActionView',{
extend: 'Ext.window.Window',
xtype: 'toolactions',
requires:[
'Tool.view.ActionController'
],
controller: 'actions',
viewModel: {},
layout: { type: 'border' },
closeAction: 'hide',
title: 'Actions',
store: 'Task',
width: 1500,
height: 800,
items: [{
id: 'ChangeLog',
xtype: 'grid',
selType: 'rowmodel',
split: true,
title: 'Log',
region: 'south',
width: 600,
height: 300,
bind: {store: '{tasks}'},
columns: {
defaults: {
width: 175
},
items: [
{ text: 'Script Command', dataIndex: 'command_script', flex: 1},
{ text: 'Output', dataIndex: 'command_output', width: 250, flex: 1 },
{ text: 'Status', dataIndex: 'state', width: 250, flex: 1 }]
},
bbar: ['->',{
xtype: 'button',
text: 'Refresh',
listeners: {
click: 'refreshActions'
}
}
]
}]
Refresh Code
refreshActions: function() {
var me = this;
this.currentRecord.tasks().load(function(records, operation, success) {
if(!operation.wasSuccessful()) {
var message = "Failed to load data from server.";
if(operation.hasException())
message = message + " " + operation.getError();
var app = Tool.getApplication();
app.toast(message,'error');
}
else {
me.configureButtons();
me.configureAutoRefresh();
}
});
}
Detect for Auto Refresh
configureAutoRefresh: function() {
var autoRefresh = false;
var maxId = this.getMaximumId(this.currentRecord.tasks());
var maxRecord = this.currentRecord.tasks().getById(maxId);
if(maxRecord.data.state=='1' || maxRecord.data.state=='0') {
autoRefresh = true;
}
if(autoRefresh == true) {
if(this.autoRefreshTask == null) {
var me = this;
var task =
{
run: function() {
me.refreshActions();
return true;
},
interval: 2000 // 2 seconds
};
this.autoRefreshTask = Ext.TaskManager.start(task);
}
}
else if(this.autoRefreshTask != null) {
Ext.TaskManager.stop(this.autoRefreshTask);
this.autoRefreshTask = null;
}
}
As I see it you have 2 options that not necessarily exclude each other:
If a grid row is selected:
Before the store reload get the selected record using getSelection() method and after the action is complete use the ensureVisible( record, [options] ) method to scroll that record into view.
If no row is selected:
Before the store reload get the current scroll position (I assume in your case is X) using the getScrollX() method and after the action is complete use setScrollX(x, [animate]) to get back to the previous scroll position.

why my combo's store does not delete record

I have a tree panel. Each node in the tree has a check box. When users check a node, I add the text of the node as an option to a combo box. This much works like a charm.
When users uncheck a node, I want to remove the corresponding option from the combo box. Sometimes it removes, and sometimes it does not. I have been pulling my hair out for days now. What am I doing wrong? Thanks.
Here is my the init function in my controller:
init: function () {
this.control({
"#problemsTree": {
load: this.selectFirstProblem,
select: this.showProblemDetail,
checkchange: this.handleCheckChange
},
"#run-problems-button": { click: this.runSelectedProblems },
"#stop-problems-button": { click: this.stopSelectedProblems }
});
}
Here is handleCheckChange function in the same controler:
toggleLogOption: function(record, isChecked) {
var logStore = Ext.StoreManager.lookup("logs-store");
if(isChecked && logStore.find("text", record.data.text) == -1) {
logStore.add(record)
} else if(!isChecked) {
logStore.remove(record)
}
logStore.sync();
},
handleCheckChange: function(node, isChecked) {
if(node.isLeaf()) {
var record = Ext.create("GiipIq.model.Log", {id: node.data.id, text: node.data.text});
this.toggleLogOption(record, isChecked);
} else {
node.cascadeBy(function(nd) {
nd.set("checked", isChecked);
if(nd.isLeaf()) {
var record = Ext.create("GiipIq.model.Log", {id: nd.data.id, text: nd.data.text});
this.toggleLogOption(record, isChecked);
}
},
this
);
}
},
Here is my Log combo:
Ext.define("GiipIq.view.Log", {
extend: "Ext.window.Window",
alias: "widget.logwindow",
titleAlign: "center",
closable: false,
maximizable: true,
draggable: false,
resizable: false,
overflowX: "hidden",
border: false,
layout: 'fit',
x: (Ext.getBody().getViewSize().width/2) + 2,
y: 0,
width: (Ext.getBody().getViewSize().width/2) - 5,
height: Ext.getBody().getViewSize().height/2,
initComponent: function () {
this.items = [{
xtype: "panel",
itemId: "logPanel",
title: "Live Logs ",
tools:[{
xtype:"combo",
width: 250,
emptyText: "Filter logs",
id: "logFilter",
store: Ext.create("GiipIq.store.Logs"),
queryMode: "local",
displayField: "text",
valueField: "id"
}]
}];
this.callParent(arguments);
}
});
Here is my Log store:
Ext.define("GiipIq.store.Logs", {
extend: "Ext.data.Store",
storeId:"logs-store",
model: "GiipIq.model.Log",
sorters: [{ property: "text", direction: "ASC" }]
});
Here is my Log model:
Ext.define("GiipIq.model.Log", {
extend: "Ext.data.Model",
idProperty: "text",
fields: [
{ name: "id", type: "string"},
{ name: "text", type: "string" }
],
proxy: {
type: "localstorage",
id: "proxy-key",
listeners: {
exception: function(proxy, response, operation, opts) {
if(typeof(operation.error) == "string") {
Ext.Msg.alert("Error", "Connection to server interrupted" + operation.error);
}
}
}
}
});
You always create new record even if you are trying to remove. The logic should rely on ids, they seem to be same in tree and combo, and when removing, you should try to find the record in combo by id and remove that. Create new record only if adding.

Resources