I'm building my first platform using Ext.js, and I'm trying to figure it out how can I specify a limit of rows for my Gridpanel. I would like to show only the last 10 values from a store
At the moment, I'm receiving all the values from a store, and I'm populating the grid with this values,
I could use this:
gridStore.remove(gridStore.getAt(i))
But I can't remove the data from the store because I'm using this store to load some polylines on a map too, so I have to hide the rows instead of removing data from the store.
My Store:
Ext.define('ES.store.Timeline', {
extend: 'Ext.data.Store',
alias: 'store.timeline',
storeId: 'timeline',
fields: [
'vid', 'time', 'lat', 'lng', 'address', 'dir', 'vel', 'hidden'
],
pageSize: 500,
autoSync:true,
sorters: [
{
property: 'time',
direction: 'DESC'
}
],
data: {
query: []},
proxy: {
type: 'sessionstorage',
id: 'sessionTimeline',
reader: {
type: 'json',
rootProperty: 'query'
}
},
filters: [{ property: 'hidden', value: false }]
});
My Grid
Ext.define('ES.view.Layout.Menu.Menu', {
extend: 'Ext.grid.Panel',
alias: 'widget.timelineBar',
controller: 'menu',
viewModel: 'menu',
pageSiz: '10',
id: 'timelineBar',
autoScroll: true,
title: 'Timeline',
store: {
type: 'timeline'
},
columns: {
border: false,
defaults: {
hoverCls: ''
},
items: [{ ...
}]
}
});
Thank you
pageSize is property of store, not grid (and you have typo in your code - pageSiz).
I think you can use memory proxy with enablePaging instead of sessionstorage proxy. Also you might need Ext.toolbar.Paging.
Check this fiddle.
You could also used a chained store, to be sure to not alterate the main store.
In the ViewModel of your page, you could use code like this :
initConfig: function(instanceConfig) {
var me = this,
config = {
stores: {
MainStore: {
storeId: 'MainStore',
model: ...,
proxy: {
...
})
},
LastUsed: {
autoDestroy: true,
source: 'MainStore',
// paging doesn't work on chained store
filters: [
function(record) {
let store = me.getStore('LastUsed'),
recordIndex = store.getRange().indexOf(record),
lastUsedCount = 10;
if((recordIndex+1) > lastUsedCount) {
return false
}
return true;
}
]
}
}
};
if (instanceConfig) {
me.getConfigurator().merge(me, config, instanceConfig);
}
return me.callParent([config]);
}
use pageSize property of store.if you want show only 10 records.
but if you want show last 10 records then you have to pass filter.
filters: [
function(record) {
<condition>
return true/false;
}
]
Related
To reproduce this issue, I created a fiddle: https://fiddle.sencha.com/#view/editor&fiddle/3mk0 . The steps to reproduce are as follows:
Open your favorite browser and open the debugging tools to be able to view the JavaScript console
Navigate to the fiddle and run it
Double-click on a row - the grid is empty. Check the JS console: Uncaught TypeError: store is null (I used FF). The crux of the issue is that, in this line, let store = vm.getStore('testStore');, vm.getStore('testStore') returns null the first time. That's what I am trying to understand - why doesn't ExtJs initialize completely the ViewModel and the store, and instead it returns null. The problem is the binding in the url of the proxy. If the store doesn't have any binding in the url, it's going to work the first time as well.
Close the window, and double-click again on a row. This time the grid will show the data.
I know how to fix this - if I issue a vm.notify() before I set the store, it's going to work properly (I added a commented out line in the code).
Here is the source code from the fiddle:
app.js
/// Models
Ext.define('App.model.GroupRecord', {
extend: 'Ext.data.Model',
alias: 'model.grouprecord',
requires: [
'Ext.data.field.Integer',
'Ext.data.field.String'
],
fields: [{
type: 'int',
name: 'id'
}, {
type: 'string',
name: 'description'
}]
});
Ext.define('App.model.SomeRecord', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.field.Integer',
'Ext.data.field.String'
],
fields: [{
type: 'int',
name: 'id'
}, {
type: 'string',
name: 'description'
}, {
type: 'int',
name: 'groupId'
}]
});
//// SomeGridPanel
Ext.define('App.view.SomeGridPanel', {
extend: 'Ext.grid.Panel',
alias: 'widget.somegridpanel',
requires: [
'App.view.SomeGridPanelViewModel',
'App.view.SomeGridPanelViewController',
'Ext.view.Table',
'Ext.grid.column.Number'
],
controller: 'somegridpanel',
viewModel: {
type: 'somegridpanel'
},
bind: {
store: '{testStore}'
},
columns: [{
xtype: 'numbercolumn',
dataIndex: 'id',
text: 'ID',
format: '0'
}, {
xtype: 'gridcolumn',
dataIndex: 'description',
text: 'Description'
}]
});
Ext.define('App.view.SomeGridPanelViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.somegridpanel',
requires: [
'Ext.data.Store',
'Ext.data.proxy.Ajax',
'Ext.data.reader.Json'
],
data: {
groupId: null
},
stores: {
testStore: {
model: 'App.model.SomeRecord',
proxy: {
type: 'ajax',
url: 'data1.json?groupId={groupId}',
reader: {
type: 'json'
}
}
}
}
});
Ext.define('App.view.SomeGridPanelViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.somegridpanel',
onRefresh: function (groupId) {
console.log('calling the grid panel onrefresh for groupId: ' + groupId);
let vm = this.getViewModel();
console.log(vm);
vm.set('groupId', groupId);
//vm.notify(); // <- uncomment this line to make it work properly
let store = vm.getStore('testStore');
//tore.proxy.extraParams.groupId = groupId;
store.load();
}
});
// TestWindow
Ext.define('App.view.TestWindow', {
extend: 'Ext.window.Window',
alias: 'widget.testwindow',
requires: [
'App.view.TestWindowViewModel',
'App.view.TestWindowViewController',
'App.view.SomeGridPanel',
'Ext.grid.Panel'
],
controller: 'testwindow',
viewModel: {
type: 'testwindow'
},
height: 323,
width: 572,
layout: 'fit',
closeAction: 'hide',
bind: {
title: '{title}'
},
items: [{
xtype: 'somegridpanel',
reference: 'someGrid'
}]
})
Ext.define('App.view.TestWindowViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.testwindow',
data: {
title: 'Test Window'
}
});
Ext.define('App.view.TestWindowViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.testwindow',
listen: {
controller: {
'*': {
loadData: 'onLoadData'
}
}
},
onLoadData: function (groupRecord) {
console.log('Loading data...');
console.log(groupRecord);
let vm = this.getViewModel();
vm.set('title', 'Group: ' + groupRecord.get('description'));
this.lookup('someGrid').getController().onRefresh(groupRecord.get('id'));
}
});
// ViewPort
Ext.define('App.view.MainViewport', {
extend: 'Ext.container.Viewport',
alias: 'widget.mainviewport',
requires: [
'App.view.MainViewportViewModel',
'App.view.MainViewportViewController',
'Ext.grid.Panel',
'Ext.view.Table',
'Ext.grid.column.Number'
],
controller: 'mainviewport',
viewModel: {
type: 'mainviewport'
},
height: 250,
width: 400,
items: [{
xtype: 'gridpanel',
title: 'Main Grid - double-click on a row',
bind: {
store: '{groupRecords}'
},
columns: [{
xtype: 'numbercolumn',
dataIndex: 'id',
text: 'Group id',
format: '00'
}, {
xtype: 'gridcolumn',
flex: 1,
dataIndex: 'description',
text: 'Group'
}],
listeners: {
rowdblclick: 'onGridpanelRowDblClick'
}
}]
});
Ext.define('App.view.MainViewportViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.mainviewport',
requires: [
'Ext.data.Store',
'Ext.data.proxy.Memory'
],
stores: {
groupRecords: {
model: 'App.model.GroupRecord',
data: [{
id: 1,
description: 'Group 1'
}, {
id: 2,
description: 'Group 2'
}, {
id: 3,
description: 'Group 3'
}, {
id: 4,
description: 'Group 4'
}, {
id: 5,
description: 'Group 5'
}],
proxy: {
type: 'memory'
}
}
}
});
Ext.define('App.view.MainViewportViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.mainviewport',
onGridpanelRowDblClick: function (tableview, record, element, rowIndex, e, eOpts) {
if (!this._testWindow) {
this._testWindow = Ext.create('widget.testwindow');
}
this._testWindow.show();
this.fireEvent('loadData', record);
}
});
Ext.application({
//name : 'Fiddle',
models: [
'SomeRecord',
'GroupRecord'
],
views: [
'MainViewport',
'TestWindow',
'SomeGridPanel'
],
name: 'App',
launch: function () {
Ext.create('App.view.MainViewport');
}
});
}
data1.json:
function(params, req, Fiddle) {
var range = [];
for (var i = 1; i <= 50; i++) {
var obj = {
id: i,
description: `Item ${i}`,
groupId: Math.floor( (i - 1) / 10) + 1
}
range.push(obj);
}
let allData = range.filter(it => it.groupId === params.groupId )
//console.log(params);
//console.log(req);
return allData;
}
The code is a bit contrived but it follows an issue (though not identical) I had in the real app. In the real app I have a weird intermittent issue, where I have a complex form with subcomponents, and a vm.notify() call made in a subcomponent, fires a method bound to a ViewModel data member (in other words it fires when that data member changes), which in turn tries to refresh a local store in another subcomponent but this subcomponent's ViewModel getStore() call returns null. The proxy of that store has the url property bound to a ViewModel data member. That's the common pattern. It seems to me that stores with proxies that have the url property bound to a ViewModel data property (ex: url: 'data1.json?groupId={groupId}') are not initialized properly by the time the form is rendered, and eventually, after some ViewModel computation cycles, they get initialized finally.
TIA
Update: There was a question below whether the url attribute of the proxy is bindable. I think it is bindable. Sencha Architect shows it as bindable, though, when enabled, it doesn't place the property in a bind object.
I did search the ExtJs 7.6.0 code base for samples where the {...} expressions are used in the url attribute, and I stumbled upon this test case:
packages\core\test\specs\app\ViewModel.js from line 7237:
describe("initial", function() {
it("should not create the store until a required binding is present", function() {
viewModel.setStores({
users: {
model: 'spec.User',
proxy: {
type: 'ajax',
url: '{theUrl}'
}
}
});
notify();
expect(viewModel.getStore('users')).toBeNull();
setNotify('theUrl', '/foo');
var store = viewModel.getStore('users');
expect(store.isStore).toBe(true);
expect(store.getProxy().getUrl()).toBe('/foo');
});
it("should wait for all required bindings", function() {
viewModel.setStores({
users: {
model: 'spec.User',
proxy: {
type: 'ajax',
url: '{theUrl}',
extraParams: {
id: '{theId}'
}
}
}
});
notify();
expect(viewModel.getStore('users')).toBeNull();
setNotify('theUrl', '/foo');
expect(viewModel.getStore('users')).toBeNull();
setNotify('theId', 12);
var store = viewModel.getStore('users');
expect(store.isStore).toBe(true);
expect(store.getProxy().getUrl()).toBe('/foo');
expect(store.getProxy().getExtraParams().id).toBe(12);
});
});
What is interesting is that it is expected in the test that viewmodel getStore would return null before setting the value for theUrl: expect(viewModel.getStore('users')).toBeNull(); !
Maybe it is by design...
Reason for the issue is - triggered event listener is getting executed before the View Model is initialised and grid is rendered
We can use following approach to fix these issues
Add delay before triggering the event
Listen for initViewModel method in controller and then load the store
Or add afterrender event on the grid, read its store and then load it
// Add delay to ensure view model is initialized
Ext.defer(function() {
this.fireEvent('loadData', record);
}, 1000, this);
There is a override function called "init" in each controller so use your initial code in this function. This function invokes every time when UI refresh/added new.
Example:
init: function() {
var vm = this.getViewModel();
var store = vm.data.myCurrentStore;
store.load();
}
I am using Ext JS 7.1 Modern, and I want to know if there is a way to filter a store also locally which has remoteFilter: true.
I have tried to explain my case with a Sencha Fiddle and also attached the code below.
The example contains a store that simulates remote filtering to a server by using dynamic data defined in names.json and setting remoteFilter and filters config.
remoteFilter: true,
filters: {
property: 'id',
value: 1
}
After the data is retrieved, suppose the user wants to filter the retrieved data locally by typing into the 'search' field. Now, I want to filter the store locally, but since remoteFilter is set to true, the filter that I set sends a request to the server instead of doing local filtering.
Is there a straightforward way to realize this use-case? It feels like a very common use-case that everybody would need, so I wanted to be sure that I am not missing something.
app.js
Ext.define('App.store.TestStore', {
extend: 'Ext.data.Store',
alias: 'store.teststore',
fields: ['id', 'firstName', 'lastName'],
autoLoad: true,
proxy: {
type: 'ajax',
url: 'names.json',
reader: {
type: 'json',
rootProperty: 'data'
}
},
remoteFilter: true,
filters: {
property: 'id',
value: 1
}
});
Ext.define('App.controller.TestController', {
extend: 'Ext.app.ViewController',
alias: 'controller.testcontroller',
doSearch: function(field) {
var list = this.lookup('list'),
store = list.getStore(),
value = field.getValue();
var filters = store.getFilters();
if (Ext.isEmpty(value))
filters.remove('firstName')
else {
filters.add(new Ext.util.Filter({
property: 'firstName',
value: value,
operator: 'in'
}))
}
}
});
Ext.define('App.dataview.TestList', {
extend: 'Ext.dataview.List',
xtype: 'testlist',
store: {
type: 'teststore'
},
itemTpl: '<div class="contact">{id} <b>{firstName} {lastName}</b></div>'
});
Ext.define('App.MainView', {
extend: 'Ext.Panel',
controller: 'testcontroller',
fullscreen: true,
items: [{
xtype: 'searchfield',
ui: 'solo',
placeholder: 'Search names',
listeners: {
buffer: 500,
change: 'doSearch'
}
}, {
reference: 'list',
xtype: 'testlist'
}]
})
Ext.application({
name: 'App',
mainView: 'App.MainView',
launch: function () {
}
});
names.json
var data = [{
id: 1,
firstName: 'Peter',
lastName: 'Venkman'
}, {
id: 2,
firstName: 'Raymond',
lastName: 'Stantz'
}, {
id: 3,
firstName: 'Egon',
lastName: 'Spengler'
}, {
id: 4,
firstName: 'Winston',
lastName: 'Zeddemore'
}]
var results = data.filter(function(record) {
if (params.filter) {
return record.id > params.filter[0].value
}
})
return {
"success": true,
"data": results
}
Thanks in advance
Use the chained store functionality. Filter your main store remotely, then create a chained store based off the main store to apply extra local filters.
Evan is right. In early version Extjs we can loaded remoteFilter: true, and local filtered data with help method filterBy. Cleared filter local we can call clearFilter method with argument true.
But in latest version that functional was removed.
onFilterEndUpdate method
if (me.getRemoteFilter()) {
me.getFilters().each(function(filter) {
if (filter.getInitialConfig().filterFn) {
Ext.raise('Unable to use a filtering function in conjunction with ' + 'remote filtering.');
}
});
....
}
How to use chainedstore you can see in my fiddle
I have the below code that I am trying to get the list of dates using Ajax and display those on the page as links to elsewhere. So each entry would be a link that when you click would take you elsewhere. Though the treelist is not loading any items...
Data
{
"success": true,
"data": [
"2018-10-08T00:00:00",
"2018-10-05T00:00:00",
"2018-10-04T00:00:00",
"2018-10-03T00:00:00",
]
}
Code
Ext.define('...', {
extend: 'Ext.form.Panel',
xtype: '...',
requires: [
'...'
],
layout: 'border',
items: [{
xtype: 'container',
store: {
proxy: {
type: 'ajax',
url: '...',
useDefaultXhrHeader: true,
withCredentials: true,
reader: {
type: 'json',
rootProperty: 'data'
},
}
}
}]
});
You can display a grid with your data. It can show clickable links.
To do this you need to make a grid with your ajax store and a grid renderer like this:
// Ajax store
Ext.create('Ext.data.Store', {
storeId: 'mystore',
autoLoad: true,
proxy: {
type : 'ajax',
url : '/file.php',
actionMethods: {
read: 'POST'
},
reader : {
type: 'json'
},
extraParams : {
key : 'val'
},
fields : [
{name: 'date', type: 'string'}
]
}
})
// Grid
Ext.create('Ext.grid.Panel', {
renderTo: Ext.getBody(),
scrollable: true,
store: {
type: 'mystore'
},
columns: [
{
text: 'link column',
dataIndex:'link',
renderer: function(value) {
if(value) {
// here you can format your output
return ''+value+'';
}
}
}
]
})
Please look whole example in a Fiddle
I have a combo with a remote store in the modal window. To obtain the data is necessary to send an extra parameter to the store. This parameter is stored as a property of the window. How can I pass it?
This is my app.js file (I use MVC model, atleast try to use it :) ):
Ext.application( {
requires: [
'Ext.Ajax'
],
autoCreateViewport: true,
name: 'PM',
stores: [
...
'SourceIps',
...
],
models: [
...
],
controllers: [
...
],
init: function() {
}
} );
How I show window:
showAddRProbe: function () {
var data = {
'deviceId': this.getProbesDeviceCombo().getValue()
};
var addProbe = Ext.create( 'PM.view.AddProbe', [true, data] );
addProbe.show();
}
Window:
Ext.define( 'PM.view.AddProbe', {
extend: 'Ext.window.Window',
...
constructor: function( data ) {
this.newProbe = data[0];
this.probeData = data[1];
...
Required parameter passed as probeData.data.deviceId
Combo:
{
xtype: 'combo',
allowBlank: false,
blankText: Locale.gettext( 'Please select a Source IP' ),
fieldLabel: Locale.gettext( 'Source IP' ),
name: 'sourceIP',
triggerAction: 'all',
store: 'SourceIps',
value: Ext.getStore( 'SourceIps' ).getAt(0).get('id'),
valueField: 'id',
displayField: 'name',
queryMode: 'local'
}
Store:
Ext.define('PM.store.SourceIps', {
extend: 'Ext.data.Store',
model: 'PM.model.IdName',
autoLoad: true,
proxy: {
type: 'ajax',
api: {
read: 'data/getDeviceIps.php'
},
reader: {
type: 'json',
root: 'data',
successProperty: 'success',
messageProperty: 'message'
}
}
});
I tired to add extraParam as follow, but it does not work:
var probeData = this.getProbeWindow().probeData.data;
this.getSourceIpCombo().getStore().getProxy().setExtraParam( 'deviceId', probeData.deviceId );
this.getSourceIpCombo().getStore().load();
Use
this.getSourceIpCombo().getStore().load({
params: {
deviceId : probeData.deviceId
}
});
You could add storeId property to your store and then use
var store = Ext.data.StoreManager.lookup('myStore');
and use this to apply extra params:
Ext.apply(store.getProxy().extraParams, {
foo : 'bar',
...
});
or
store.getProxy().setExtraParam("countriesId", 1) // add parameter 'countriesId' = 1
I'm new to ExtJS but I am unable find anyway to populate my grid with a simple JSON array of strings. My JSON looks like this:
{
...stuff...
"users": ["kevin","ryan", "david", "mark", "ed"]
...more stuff...
}
I want to populate a simple one column grid with this list of users, but can't seem to find any way to do it! :(
My code looks like this:
Ext.define('usersModel', {
extend: 'Ext.data.Model',
fields: [{ name: 'users', type: 'auto'}]
});
var usersStore = Ext.create('Ext.data.Store', {
model: 'usersModel',
proxy: {
type: 'ajax',
url : '/users',
reader: 'json'
},
autoLoad: true
});
// create the Grid
Ext.create('Ext.grid.Panel', {
store: usersStore,
columns: [{
dataIndex: 'users',
width: 500,
text: 'User'
},
],
});
But it just displays a comma separated list of the users in one cell. I can see why it would do that, I'm setting the dataIndex of the column to be the array, not the elements in the array, but have no idea how to fix it.
Try this:
Ext.define('usersModel', {
extend: 'Ext.data.Model',
fields: [{ name: 'name', type: 'auto'}]
});
var usersStore = Ext.create('Ext.data.Store', {
model: 'usersModel',
proxy: {
type: 'ajax',
url : '/users',
reader: {
type: 'json',
rootProperty: 'users'
}
},
autoLoad: true
});
// create the Grid
Ext.create('Ext.grid.Panel', {
store: usersStore,
columns: [{
dataIndex: 'name',
width: 500,
text: 'User'
},
],
});