Based on this fix: http://bit.ly/1Pf59ui, you have to extend "Ext.data.proxy.Ajax". However, one of my stores look like this:
Ext.define('EcommBackoffice.store.Banks',{
extend:'Ext.data.Store',
model:'EcommBackoffice.model.BankModel',
storeId: 'bank-store-id',
autoLoad:false,
sorters: [{
property: 'name',
direction:'ASC'
}],
config: {
loaded:false
},
isLoaded: function(){
return this.getLoaded()
},
listeners: {
load: function(currentthis, records, successful){
if(successful === true){
this.setLoaded(true)
}
}
},
proxy: {
$configStrict: false,
type: 'rest',
id: 'app-banks',
url: EcommBackoffice.Global.getAPIEndPoints().banks,
reader: {
type: 'json',
rootProperty: 'bank'
},
api:{
create: EcommBackoffice.Global.getAPIEndPoints().banks,
update: EcommBackoffice.Global.getAPIEndPoints().banks,
destroy: EcommBackoffice.Global.getAPIEndPoints().banks
},
listeners: {
exception: function(proxy, response, op) {
var oBankStore = Ext.getStore('bank-store-id');
if(response.status === 403 || response.status === 401 || response.status === 503) return; /*skip this exception handler and refer to Applciation Controller handler*/
Ext.Msg.alert('ERROR', response.responseText + ' ' +response.statusText);
}
},
afterRequest:function(request,success){
if(request.method = 'POST' && success){
var oAddBankWindow = Ext.ComponentQuery.query('#add-bank-window');
if(oAddBankWindow[0]) {
oAddBankWindow[0].down('#msgPanel').update('<span style="">New bank added</span>');
oAddBankWindow[0].down('#countryName').update('');
oAddBankWindow[0].down('form').getForm().reset();
}
}
}
}
});
How can I extend "Ext.data.proxy.Ajax" if I am already extending "Ext.data.Store?" When i use $configStrict to allow the override, the current error gets replaced with this one:
Uncaught TypeError: sorter.getRoot is not a function
What should be the proper way of fixing this? I am currently upgrading an existing ExtJS 4.2 application to 6.x.
Ext.data.Store and Ext.data.proxy.Ajax are two different class not for the same purpose. Store is using the proxy.
So the way I'll do it in ExtJs 6 is to create a custom proxy, extending the REST proxy (in your case), and set the type to your custom proxy.
/**
* Custom Proxy
**/
Ext.define('EcommBackoffice.proxy.MyRestProxy', {
extend: 'Ext.data.proxy.Rest',
alias: 'proxy.bank-proxy',
/**
* #override
**/
afterRequest:function(request,success){
this.callParent(arguments); // call to parent as this is a template method
if(request.method = 'POST' && success){
var oAddBankWindow = Ext.ComponentQuery.query('#add-bank-window');
if(oAddBankWindow[0]) {
oAddBankWindow[0].down('#msgPanel').update('<span style="">New bank added</span>');
oAddBankWindow[0].down('#countryName').update('');
oAddBankWindow[0].down('form').getForm().reset();
}
}
}
});
Ext.define('EcommBackoffice.store.Banks',{
extend:'Ext.data.Store',
requires: ['EcommBackoffice.proxy.MyRestProxy'],
model:'EcommBackoffice.model.BankModel',
storeId: 'bank-store-id',
autoLoad:false,
sorters: [{
property: 'name',
direction:'ASC'
}],
// Already existing in Extjs 6
// config: {
// loaded:false
// },
// isLoaded: function(){
// return this.getLoaded()
// },
// listeners: {
// load: function(currentthis, records, successful){
// if(successful === true){
// this.setLoaded(true)
// }
// }
// },
proxy: {
type: 'bank-proxy',
id: 'app-banks',
url: EcommBackoffice.Global.getAPIEndPoints().banks,
reader: {
type: 'json',
rootProperty: 'bank'
},
api:{
create: EcommBackoffice.Global.getAPIEndPoints().banks,
update: EcommBackoffice.Global.getAPIEndPoints().banks,
destroy: EcommBackoffice.Global.getAPIEndPoints().banks
},
listeners: {
exception: function(proxy, response, op) {
var oBankStore = Ext.getStore('bank-store-id');
if(response.status === 403 || response.status === 401 || response.status === 503) return; /*skip this exception handler and refer to Applciation Controller handler*/
Ext.Msg.alert('ERROR', response.responseText + ' ' +response.statusText);
}
}
}
});
Related
Using Ext JS 7.1 Modern, I have prepared an example to show the problem.
When I have remote filters on my main store, binding the dataview.List to a ChainedStore correctly handles my local filtering. However, when I also add a PullRefresh plugin to the list, I get an error during pull refresh. I see from the source code that the plugin doesn't consider the possibility that a list's store can be a ChainedStore.
I have tried to explain the problem with a Sencha Fiddle and also attached the code below.
I have temporarily solved the problem by overriding the fetchLatest and onLatestFetched methods of Ext.dataview.pullrefresh.PullRefresh plugin, to use the source store if the list's store is a ChainedStore. But I believe the source code must be updated to handle this case.
app.js
Ext.define('App.model.Test', {
extend: 'Ext.data.Model',
fields: ['id', 'firstName', 'lastName']
});
Ext.define('App.store.Test', {
extend: 'Ext.data.Store',
alias: 'store.teststore',
model: 'App.model.Test'
});
Ext.define('App.viewmodel.Test', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.test',
data: {
query: ''
},
stores: {
test: {
type: 'teststore',
autoLoad: true,
proxy: {
type: 'ajax',
url: 'names.json',
reader: {
type: 'json',
rootProperty: 'data'
}
},
remoteFilter: true,
filters: {
property: 'id',
value: 1
}
},
chained: {
type: 'chained',
autoLoad: true,
source: '{test}'
}
}
});
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();
if (Ext.isEmpty(value)) {
store.removeFilter('firstName')
} else {
store.filter([{
property: 'firstName',
value: value,
operator: 'like'
}])
}
}
});
Ext.define('App.dataview.TestList', {
extend: 'Ext.dataview.List',
xtype: 'testlist',
viewModel: {
type: 'test'
},
plugins: [{
type: 'pullrefresh',
mergeData: false
}],
emptyText: 'Name not found',
bind: {
store: '{chained}'
},
itemTpl: '<div class="contact">{id} <b>{firstName} {lastName}</b></div>'
});
Ext.define('App.MainView', {
extend: 'Ext.Panel',
controller: 'testcontroller',
fullscreen: true,
viewModel: {
type: 'test'
},
items: [{
xtype: 'searchfield',
ui: 'solo',
placeholder: 'Search names',
listeners: {
buffer: 500,
change: 'doSearch'
},
bind: {
value: '{query}'
}
}, {
reference: 'list',
xtype: 'testlist'
}]
})
Ext.application({
name: 'App',
mainView: 'App.MainView'
});
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
}
App.override.dataview.pullrefresh.PullRefresh:
Ext.define('App.override.dataview.pullrefresh.PullRefresh', {
override: 'Ext.dataview.pullrefresh.PullRefresh',
privates: {
fetchLatest: function() {
const store = this.getStore().isChainedStore ? this.getStore().getSource() : this.getStore()
store.fetch({
page: 1,
start: 0,
callback: this.onLatestFetched,
scope: this
});
},
onLatestFetched: function(newRecords, operation, success) {
var me = this,
list = me.getList(),
store = list.getStore().isChainedStore ? list.getStore().getSource() : list.getStore(),
length, toInsert,
oldRecords, newRecord, oldRecord, i;
if (success) {
if (me.getMergeData()) {
oldRecords = store.getData();
toInsert = [];
length = newRecords.length;
for (i = 0; i < length; i++) {
newRecord = newRecords[i];
oldRecord = oldRecords.getByKey(newRecord.getId());
if (oldRecord) {
oldRecord.set(newRecord.getData());
}
else {
toInsert.push(newRecord);
}
}
store.insert(0, toInsert);
}
else {
store.loadRecords(newRecords);
}
me.setLastUpdated(new Date());
}
me.setState('loaded');
list.fireEvent('latestfetched', me, toInsert || newRecords);
if (me.getAutoSnapBack()) {
me.snapBack(true);
}
}
}
})
Thanks in advance
Since this post, instead of being a question, was a bug report with a possible solution, it has been posted to Ext JS 7.x Community Forums\Ext JS 7.x Bugs.
The above solution, that overwrites the plugin where source store is needed, works if anyone comes across the same issue.
I have tried to refresh the Grid every 5 or 10 seconds which is using REST Proxy, but the grid is not getting refreshed more than once. Please find the code which we have tried.
Ext.define('App.Store.DeviceStore', {
extend: 'Ext.data.Store',
requires: [
'Ext.data.proxy.Ajax',
'Ext.data.reader.Json'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
storeId: 'app.store.DeviceStore',
model: 'App.model.DeviceModel',
activeRefreshTask:false,
pageSize: 5,
autoLoad: {
pageSize: 5
}
}, cfg)]);
},listeners:{
'load':function(store,records,successful,operation){
if(successful === true && store.activeRefreshTask === false){
var task = {
identifyId: 'deviceListStore',
run: function() {
if (App.app._currentPage == 'devicesform') {
store.reload();
} else {
Ext.TaskManager.stop(this);
}
},
interval: '10000'
}
Ext.TaskManager.start(task);
store.activeRefreshTask = true;
}
}
}
});
The model for the above store is
Ext.define('App.model.DeviceModel', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.field.String'
],
proxy:{
type:'rest',
reader: {
type: 'json',
rootProperty: 'data',
totalProperty:'total'
},
useDefaultXhrHeader: false,
headers:{'Content-Type':'application/json'},
api: {
read: 'url given gere'
}
},
fields: [
{
type: 'string',
name: 'id'
},
{
type: 'string',
name: 'name'
},
{
type: 'string',
name: 'desc'
},
{
type: 'string',
name: 'ipAddr'
}
]
});
I have found the issue , You have passed the interval as string in place lo number.
just change to interval: '10000' to inteval: 10000 and your taskrunner will runs fine.
var runner = new Ext.util.TaskRunner(),
updateStore , task;
updateStore = function() {
if (App.app._currentPage == 'devicesform') {
store.load();
} else {
Ext.TaskManager.stop(this);
}
};
task = runner.start({
run: updateStore ,
interval: 1000
});
probably your store.reload is sending again the same params on the request, so with a request that not change nothing changes.
Ext.define('myApp.model.SensorStation', {
extend: 'Ext.data.Model',
config: {
fields: [
{
name: 'name',
type: 'string',
mapping: '#name'
//convert: function (value, record) {
// Ext.Msg.alert(value,record.raw);
// //var nodes = rec.raw.querySelectorAll('');
//}
},
{
name: 'lat',
mapping: '#latitude',
type: 'float'
},
{
name: 'lng',
mapping: '#longitude',
type: 'float'
},
{
name: 'locid',
mapping:'#locid',
type: 'string'
}
]
},
proxy: {
type: 'ajax',
url: 'http://webtrak.bksv.com/mel/configuration',
reader: {
type: 'xml',
record:'locations',
rootProperty: 'nmts'
}
}
});
Ext.define('myApp.store.SensorStationStore', {
extend: 'Ext.data.Store',
requires: ['myApp.model.SensorStation'],
config:{
model: 'myApp.model.SensorStation',
storeId: 'SensorStore'
},
autoLoad: true
});
Ext.define('myApp.controller.SensorStations', {
extend: 'Ext.app.Controller',
requires: ['Ext.MessageBox'],
config: {
refs: {
mapComponent: 'main map'
},
control: {
mapComponent: {
maprender: 'onMaprender'
}
}
},
onMaprender: function (mapComponent, googleMap) {
var store, latlng, marker;
// Use Store name when testing on devices
// Use Store ID when testing in browsers
var sensorStore = Ext.data.StoreManager.get('SensorStore');
if (!sensorStore) {
console.log("Store not found");
Ext.Msg.alert('error', "store not found");
return;
}
//else {
// console.log("Store found");
// Ext.Msg.alert('great', "store found");
// //return;
//}
sensorStore.load({
callback: function (records, operation, success) {
mapComponent.setMapOptions({
center: new google.maps.LatLng(-37.899328, 144.843333)
});
if (success) {
Ext.Msg.alert('success', records);
}
else {
Ext.Msg.alert('error', operation);
}
Ext.Msg.alert('records.count = ' + records.length, 'haha');
}
});
}
});
The records.length is always 0.
Could you please point out where I get wrong?
Also I'd like to know the data structure of the records.
Many thanks
In your model put the proxy: {} inside config:{} and in store put autoload: trueinside config:{}
I have a request which, on success, loops through each attribute of a JSON response and adds it to my store:
var request = Ext.Ajax.request({
url: 'MCApp',
jsonData: searchquery,
params: {
start: 0,
limit: itemsPerPage
},
success: function(response) {
mainresponse = response.responseText;
if (mainresponse.length == 0) {
alert('No results returned');
return;
}
var decoded = Ext.decode(mainresponse);
for (var i = 0; i < decoded.elements.length; i++) { // loop over decoded data
var element = decoded.elements[i].element;
var model = {};
for (var x = 0; x < element.attributes.length; x++) { // loop over attributes
var attribute = element.attributes[x];
model[attribute.attrname] = attribute.attrvalue; // mapping element names & attributes
}
newstore.add(model); // implicitly cast data as Model
models[i] = model;
}
newstore.loadRawData(models);
},
failure: function() {
alert('Search Failed: Could not reach the server')
}
});
I have now recreated the requestabove within my store. What I need to do is add these same success and failure functions.
var store = Ext.create('Ext.data.Store', {
storeId: 'resultsetstore',
autoLoad: false,
pageSize: itemsPerPage,
fields: [
{ name: 'id', type: 'auto' },
{ name: 'name', type: 'auto' },
{ name: 'description', type: 'auto' }
],
proxy: {
type: 'ajaxwithpayload', //customized proxy to read "jsonData"
url: 'MCApp',
jsonData: searchquery,
reader: {
type: 'json',
root: 'elements'
}
success: { /* success functions */ },
failure: { /* failure functions */ }
}
});
Here's what my response looks like:
{
"elements":[
{
"element":{
"name":"Element Name",
"id":"Element ID",
"attributes":[
{
"attrname":"id",
"attrvalue":"This is the ID"
},
{
"attrname":"name",
"attrvalue":"This is the name"
},
//etc.
1) Is there any way to recreate these functions on my store?
2) Is decoding my response this way the best way to load my response into my store?
EDIT
I'm using the callback function when I load the store:
store.load({
params: { start: 0, limit: itemsPerPage },
callback: function(options, success, response, records) {
if (success) {
alert(response.responseText);
}
}
});
However, I'm getting an undefined in my alert, and it's telling me there are 0 records loaded. But when I look at my response in Firebug I see my JSON string returned just fine.
Error handling in stores
You can listen for the exception-event on the proxy to capture all the store errors.
And for success on the store load-event
var store = Ext.create('Ext.data.Store', {
storeId: 'resultsetstore',
autoLoad: false,
pageSize: itemsPerPage,
fields: [
{ name: 'id', type: 'auto' },
{ name: 'name', type: 'auto' },
{ name: 'description', type: 'auto' }
],
listeners: {
load: function(store, records, successful, eOpts) {
if (successfull) {
alert('success');
}
}
},
proxy: {
type: 'ajaxwithpayload', //customized proxy to read "jsonData"
url: 'MCApp',
jsonData: searchquery,
reader: {
type: 'json',
root: 'elements'
},
listeners: {
exception: function(proxy, response, operation, eOpts) {
alert('exception');
}
}
}
});
or in the load call itself:
store.load({
callback: function(records, operation, success) {
// ...
}
});
or if you use sync (for saving removed, modified,...)
store.sync({
callback: function(batch, options) {
// ...
},
success: function(batch, options) {
// ...
},
failure: function(batch, options) {
// ...
}
});
Why would a record persisted successfully with a rest proxy calling the POST/Create action, and then on subsequent saves, call the same POST/Create method and not the the PUT/Update one?
POSTS
var ref = beginQuestionnaireControl.getPegfileRef();
var Pegfile = Ext.create('Pegfect.model.Pegfile', {
Id: 0,
Reference: ref
});
Pegfile.save({
scope: this,
success: function (pegfile, operation) {
this.activePegfile = pegfile;
this.startQuestions();
},
failure: function () {
alert('That ref is not unique');
}
});
also POSTS (expecting a PUT)
this.activePegfile.save({
success: function () {
successCallback();
},
failure: function () {
alert('oops, error saving Pegfile');
}
});
proxy
proxy:
{
type: 'rest',
url: 'Pegfile',
timeout: 120000,
noCache: false,
reader:
{
type: 'json',
root: 'data',
successProperty: 'success'
},
writer:
{
type: 'json',
writeAllFields: true
}
}
The trick is to define an 'Id' property on the Model:
idProperty: 'Id'
Crying out for some convention over config on this one...