Related
I have a list of grids that can change their data in form by end-user.
Finally, I want to sync all the grids by clicking the button, then an operation is performed.
I wrote the code below:
$.when.apply(
Ext.ComponentQuery.query('grid')
.forEach(function(item) {
if (item.getXType() == "grid") {
if (item.store.getNewRecords().length > 0 || item.store.getUpdatedRecords().length > 0 || item.store.getRemovedRecords().length > 0) {
item.store.sync();
}
}
})).then(function (results) {
//do something
});
Problem is here that store.sync() not waiting for callback.
What is the recommended way of doing this?
I do it with Promise like this:
// Sync grid data if exist dirty data
Promise.all(
Ext.ComponentQuery.query('grid')
.map(grid => grid.getStore())
.filter(s => (s.getNewRecords().length + s.getUpdatedRecords().length + s.getRemovedRecords().length) > 0)
.map(s => new Promise((resolve, reject) => {
s.sync({
success: () => { resolve(); },
failure: () => { reject(); }
});
}))
).then(() => {
//do something
});
You could use callback for your store.sync() method.
The callback function to be called upon completion of the sync. The callback is called regardless of success or failure and is passed the following parameters: (batch, options).
You could achieve your requirement like this
Take a blank array name before loop. like this var gridIds=[].
In side of loop before store.sync() push grid id in above array.
Now in callback function remove that grid id from above array and check condition array is blank then your all store sync response has came.
You can check here with working Fiddle
Note I have used dummy api. Please use your actual api.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.define('MyStore', {
extend: 'Ext.data.Store',
alias: 'store.mystore',
fields: ['name'],
autoLoad: true,
pageSize: 25,
remoteSort: true,
proxy: {
type: 'ajax',
method: 'POST',
api: {
read: 'data.json',
update: 'your_update_api',
create: 'your_create_api',
destroy: 'your_delete_api'
},
reader: {
type: 'json'
},
writer: {
type: 'json',
encode: true,
root: 'data'
}
},
});
Ext.define('MyGrid', {
extend: 'Ext.grid.Panel',
alias: 'widget.mygrid',
store: {
type: 'mystore'
},
height: 200,
border: true,
tools: [{
xtype: 'button',
iconCls: 'fa fa-plus-circle',
tooltip: 'Add New Record',
handler: function () {
let grid = this.up('grid'),
store = grid.getStore();
store.insert(0, {
name: 'Test ' + (store.getCount() + 1)
});
}
}],
columns: [{
text: 'Name',
dataIndex: 'name',
flex: 1
}]
});
Ext.create({
xtype: 'panel',
// title: 'Store sync example',
items: [{
xtype: 'mygrid',
title: 'Grid 1'
}, {
xtype: 'mygrid',
title: 'Grid 2'
}, {
xtype: 'mygrid',
title: 'Grid 3'
}, {
xtype: 'mygrid',
title: 'Grid 4'
}],
bbar: ['->', {
text: 'Submit Changes',
handler: function (btn) {
var panel = btn.up('panel'),
grids = panel.query('grid'),
gtidIds = [],
lenthCheck = function (arr) {
return arr.length > 0;
};
grids.forEach(function (grid) {
let store = grid.getStore();
if (lenthCheck(store.getNewRecords()) || lenthCheck(store.getUpdatedRecords()) || lenthCheck(store.getRemovedRecords())) {
panel.mask('Please wait...');
gtidIds.push(grid.getId());
store.sync({
callback: function () {
Ext.Array.remove(gtidIds, grid.getId());
if (gtidIds.length == 0) {
panel.unmask();
Ext.Msg.alert('Info', 'All grid store sync success.');
}
}
}, grid);
}
});
}
}],
renderTo: Ext.getBody(),
})
}
});
Inside the ViewModel I've defined 2 stores and I'm using a gridpanel as view. Is there any chance to state if else condition for bind property inside gridpanel?
ViewModel:
stores: {
dataStore: {
model: 'MyApp.first.Model',
autoLoad: true,
session: true
},
listStore: {
model: 'MyApp.second.Model',
autoLoad: true,
session: true,
},
and on grid panel I want to do this condition;
Ext.define('MyApp.base.Grid', {
extend: 'Ext.grid.Panel',
// Currently binds directly to listStore
bind: '{listStore}',
// but I'm trying to implement a proper adjustment such as this;
// bind: function () {
// var username = localStorage.getItem('username');
// if (username === 'sample#adress.com') {'{dataStore}';} else {'{listStore}'}
// },
You can't use conditional expressions with bind, however, you can use ViewModel's formulas to select which store to use with grid. Here is example of such formula:
conditionalStore: function (get) {
var param = get('param');
if (param === 1) {
return get('simpsonsStore');
}
return get('griffinsStore');
}
And here is working fiddle, which you can play with: https://fiddle.sencha.com/#view/editor&fiddle/2eq2
You can use bindStore method of grid.
In this FIDDLE, I have created a demo using grid. I hope this will help/guide you to achieve your requirement.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
//defining store 1
Ext.define('Store1', {
extend: 'Ext.data.Store',
alias: 'store.store1',
autoLoad: true,
fields: ['name', 'email', 'phone'],
proxy: {
type: 'ajax',
url: 'data1.json',
reader: {
type: 'json',
rootProperty: ''
}
}
});
//defining store 2
Ext.define('Store2', {
extend: 'Ext.data.Store',
alias: 'store.store2',
autoLoad: true,
fields: ['name', 'email', 'phone'],
proxy: {
type: 'ajax',
url: 'data2.json',
reader: {
type: 'json',
rootProperty: ''
}
}
});
//defining view model
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myvm',
stores: {
gridStore1: {
type: 'store1'
},
gridStore2: {
type: 'store2'
}
}
});
//creating grid
Ext.create({
xtype: 'grid',
title: 'Example of bind the store',
renderTo: Ext.getBody(),
viewModel: {
type: 'myvm'
},
columns: [{
text: 'Name',
dataIndex: 'name'
}, {
text: 'Email',
dataIndex: 'email',
flex: 1
}, {
text: 'Phone',
dataIndex: 'phone'
}],
tbar: [{
text: 'Load Store1',
handler: function (btn) {
var grid = this.up('grid');
grid.bindStore(grid.getViewModel().getStore('gridStore1'))
}
}, {
text: 'Load Store2',
handler: function (btn) {
var grid = this.up('grid');
grid.bindStore(grid.getViewModel().getStore('gridStore2'))
}
}]
});
}
});
I barely know something regarding ExtJS and I'm struggling a lot since some days ago.
I know code presented here might not be the most performatic one, but it's a legacy application that I had to perform support (I did not develop it from scratch).
These below are called to populate a ExtJS combobox - carregaComboVersao makes a get call to server in order to obtain a response.
carregaVAloresCombo parses the response in order to populate combobox.
me.comboVersao is my combobox component, defined in this same javascript file as:
me.comboVersao = me.down('combobox[name=idVersao]');
carregaComboVersao: function(idObra){
var me = this;
KLIFT.model.mOrcamentoVersao.load(0, {
url: '../orcamentoVersao/buscaVersoesOrcamentoPorObra',
params: {
idObra: idObra
},
success: function(record){
alert(JSON.stringify(record));
me.carregaValoresCombo(record);
me.setLoading(false);
}
});
},
carregaValoresCombo: function(record){
var novo = JSON.stringify(record);
var me = this;
if (novo !== null) {
var item = record["raw"]["data"];
var array = new Array();
for(i=0; i<item.length; i++){
obj = Ext.create('KLIFT.model.mOrcamentoVersao', {
id : item[i].id,
dthrCriacao : item[i].dthrCriacao,
descritivoVersao: item[i].descritivoVersao,
versao: item[i].versao
});
}
me.comboVersao.setValue(obj);
}
Here is the combobox defined in a form component:
{
xtype: 'combobox',
fieldLabel: 'Versão',
displayField: 'descritivoVersao',
valueField: 'descritivoVersao',
width: 300,
name: 'idVersao',
editable:false,
model: Ext.create(('KLIFT.model.mOrcamentoVersao'))
}
and here is the model:
Ext.define('KLIFT.model.mOrcamentoVersao', {
extend: 'Ext.data.Model',
fields: [
{name: 'versao', type: 'int'},
{name: 'dthrCriacao', type: 'datetime'},
{name: 'descritivoVersao', type: 'string'},
{name: 'id', type: 'int'}
]
}
);
Any help is welcome.
Just an addition - response from server comes into 'record' variable as:
{"raw":{"data":[{"id":1,"dthrCriacao":"2018-02-25T00:00:00-0300","descritivoVersao":"mais veia","versao":0},{"id":2,"dthrCriacao":"2018-02-25T00:00:00-0300","descritivoVersao":"mais intermediaria","versao":1},{"id":3,"dthrCriacao":"2018-02-25T00:00:00-0300","descritivoVersao":"mais NOVA","versao":2}]},"modified":{},"data":{"versao":0,"dthrCriacao":"","descritivoVersao":"","id":0},"hasListeners":{},"events":{},"stores":[],"phantom":false,"internalId":0,"id":"KLIFT.model.mOrcamentoVersao-ext-record-2","dirty":true}
For pushing multiple values in combobox list, use store and map the store to the combobox.Mention the model in the store definition.
var store = Ext.create('Ext.data.Store', {
model: 'KLIFT.model.mOrcamentoVersao',
});
Add the multiple model instances to the store, which in turn will be added to the combobox.
comboVersao.getStore().add(obj);
For Example:
Ext.application({
name: 'Fiddle',
launch: function() {
Ext.define('KLIFT.model.mOrcamentoVersao', {
extend: 'Ext.data.Model',
fields: [{
name: 'versao',
type: 'int'
},
{
name: 'dthrCriacao',
type: 'datetime'
},
{
name: 'descritivoVersao',
type: 'string'
},
{
name: 'id',
type: 'int'
}
]
});
var store = Ext.create('Ext.data.Store', {
model: 'KLIFT.model.mOrcamentoVersao',
});
var comboVersao = Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Versão',
displayField: 'descritivoVersao',
valueField: 'descritivoVersao',
width: 300,
name: 'idVersao',
editable: false,
store: store,
queryMode: 'local',
renderTo: Ext.getBody()
});
var carregaValoresCombo = function(record) {
var novo = JSON.stringify(record);
var me = this;
if (novo !== null) {
var item = record["raw"]["data"];
var array = new Array();
for (i = 0; i < item.length; i++) {
obj = Ext.create('KLIFT.model.mOrcamentoVersao', {
id: item[i].id,
dthrCriacao: item[i].dthrCriacao,
descritivoVersao: item[i].descritivoVersao,
versao: item[i].versao
});
comboVersao.getStore().add(obj);
}
}
}
carregaValoresCombo({"raw":{"data":[{"id":1,"dthrCriacao":"2018-02-25T00:00:00-0300","descritivoVersao":"mais veia","versao":0},{"id":2,"dthrCriacao":"2018-02-25T00:00:00-0300","descritivoVersao":"mais intermediaria","versao":1},{"id":3,"dthrCriacao":"2018-02-25T00:00:00-0300","descritivoVersao":"mais NOVA","versao":2}]},"modified":{},"data":{"versao":0,"dthrCriacao":"","descritivoVersao":"","id":0},"hasListeners":{},"events":{},"stores":[],"phantom":false,"internalId":0,"id":"KLIFT.model.mOrcamentoVersao-ext-record-2","dirty":true});
}
});
<link rel="stylesheet" href="https://cdn.sencha.com/ext/gpl/4.1.1/resources/css/ext-all.css">
<script type="text/javascript" src="https://cdn.sencha.com/ext/gpl/4.1.1/ext-all-debug.js"></script>
It seems to be very basic question, but happens to kill lot of my time.
How can I map following in to Ext.data.Model?
<Principal>STEMMED CORP.</Principal>
<BusinessDefinitions>
<value>NY CLIENTS CORE</value>
<value>US LISTED DERIVS- STOCK,ADR,ETF</value>
<value>US CLIENT SITE TRADING</value>
<value>SYNDICATES - ADRS AND CBS</value>
<value>GWM CAPITAL MARKETS</value>
</BusinessDefinitions>
<countryOfResidence>USA</countryOfResidence>
Problem is, I unable to figure out how to get array of string for BusinessDefinitions against each value.
The following field is what I have added to Model.fields:
// Business Definition(s)
{
name: 'BusinessDefinitions',
type: 'auto',
mapping: 'value',
convert: function(n, record) {
console.log('BusinessDefinition: ' + n);
return n;
}
}
I have tried other combinations as well, but nothing seem to work.
The following was fabricated to fit your data from the example below.
Here is a Sencha Fiddle of the answer I have provided. It is 4.2.1.883 compliant. I have yet to try this with version 5.1.0.
Data
<BusinessArray>
<BusinessItem>
<Principal>STEMMED CORP.</Principal>
<BusinessDefinitions>
<value>NY CLIENTS CORE</value>
<value>US LISTED DERIVS- STOCK,ADR,ETF</value>
<value>US CLIENT SITE TRADING</value>
<value>SYNDICATES - ADRS AND CBS</value>
<value>GWM CAPITAL MARKETS</value>
</BusinessDefinitions>
<countryOfResidence>USA</countryOfResidence>
</BusinessItem>
</BusinessArray>
Application
Ext.define('App.model.Business', {
requires: [ 'Ext.data.reader.Xml' ],
extend: 'Ext.data.Model',
fields: [{
name: 'principal',
mapping: 'Principal',
type: 'string'
}, {
name: 'country',
mapping: 'countryOfResidence',
type: 'string'
}, {
name: 'businessDefs',
type : 'auto',
convert: function(value, record) {
var nodes = record.raw.querySelectorAll('BusinessDefinitions value');
var items = [];
for (var i = 0; i < nodes.length; i++) {
items.push(nodes[i].textContent);
}
return items;
}
}]
});
Ext.application({
name : 'Fiddle',
launch : function() {
var store = Ext.create('Ext.data.Store', {
model: 'App.model.Business',
proxy: {
type: 'ajax',
url: 'business.xml',
reader: {
type: 'xml',
record: 'BusinessItem'
}
},
autoLoad : true
});
Ext.create('Ext.panel.Panel', {
title : 'XML Model Example',
layout : 'hbox',
items : [{
xtype: 'combo',
fieldLabel: 'Business',
emptyText: 'select',
editable: false,
queryMode: 'local',
store: store,
displayField: 'principal',
valueField: 'businessDefs',
listeners : {
select: function (combo, record, index) {
Ext.Msg.alert('Business Definitions', combo.getValue().join('<br />'));
}
}
}],
renderTo: Ext.getBody()
});
}
});
Example
The example below is from the accepted solution from Sencha Forums: How do I parse a XML node to an array of strings? Also handing XML attributes?.
XML Data
<jobs>
<job>
<id>1</id>
<name audioSrc="audio/jobs/names/electrician.mp3">Electrician</name>
<attributes>
<attributeID>sitting</attributeID>
<attributeID>individual</attributeID>
<attributeID>lightEquip</attributeID>
<attributeID>doer</attributeID>
<attributeID>physical</attributeID>
<attributeID>repair</attributeID>
</attributes>
</job>
</jobs>
Store
Ext.create('Ext.data.Store', {
model: 'App.model.JobData',
proxy: {
type: 'ajax',
url: dataURL,
reader: {
type: 'xml',
record: 'job'
}
}
});
Model
Ext.define('App.model.JobData', {
requires: [ 'Ext.data.reader.Xml' ],
extend: 'Ext.data.Model',
config: {
fields: [{
name: 'id',
mapping: 'id',
type: 'int'
}, {
name: 'title',
mapping: 'name',
type: 'string'
}, {
name: 'attributeList',
mapping : 'attributes',
convert: function(value, record) {
var nodes = record.raw.querySelectorAll('attributes attributeID');
var arrayItem = [];
var l = nodes.length;
for (var i = 0; i < l; i++) {
var node = nodes[i];
arrayItem.push(nodes[i].textContent);
console.log(nodes[i].textContent);
}
return arrayItem;
}
}]
}
});
Following is what worked for me:
No need to add requires: [ 'Ext.data.reader.Xml' ],
With that, following is the final field
},{
//Business Definition/s
name: 'BusinessDefinitions',
type: 'auto',
mapping: function(data){
return data.children[2].children[10];
},
convert: function(value, record) {
var items = [];
var nodes = record.data.BusinessDefinitions.querySelectorAll('BusinessDefinitions value');
for (var i = 0; i < nodes.length; i++) {
items.push(nodes[i].textContent);
}
return items;
}
},
I'm struggling with my application right in the beginning.
this.getScoresStore().on('load', function(score, records) {
var view = Ext.getCmp('scoreView');
view.down('form').loadRecord(records[0].data);
console.log(view.down('form').getRecord());
console.log(view.down('form').getValues());
});
After the store is loaded, I add the records to the form. Console says it's added, however the fields keep beeing empty.
Object { playerOne="301", playerTwo="301" }
Object { playerOne="", playerTwo="" }
Anyone got Ideas what could be wrong?
Controller:
Ext.define('Darts.controller.Scores', {
extend: 'Ext.app.Controller',
views: [
'score.View',
'score.Hit'
],
stores: [
'Scores'
],
models: [
'Score'
],
init: function() {
this.getScoresStore().on('load', function(score, records) {
var view = Ext.getCmp('scoreView');
view.down('form').loadRecord(records[0].data);
console.log(view.down('form').getRecord());
console.log(view.down('form').getValues());
});
this.control({
'scoreView' : {
afterrender: this.formRendered
}
});
},
formRendered: function(obj) {
console.log(obj.down('form').getRecord());
console.log('form rendered');
}
});
Views:
Ext.define('Darts.view.score.Hit' ,{
extend: 'Ext.panel.Panel',
alias : 'widget.scoreHit',
title : 'Hits',
score : 'Scores',
initComponent: function() {
this.items = [
{
xtype: 'form',
items: [
{
xtype: 'textfield',
name : 'playerTwo',
fieldLabel: 'Player 1'
}
]
}
];
this.callParent(arguments);
}
});
Ext.define('Darts.view.score.View' ,{
extend: 'Ext.panel.Panel',
alias : 'widget.scoreView',
id : 'scoreView',
title : 'Player Scores',
score : 'Scores',
initComponent: function() {
this.items = [
{
xtype: 'form',
items: [
{
xtype: 'numberfield',
name : 'playerOne',
fieldLabel: 'Player 1'
}, {
xtype: 'textfield',
name : 'playerTwo',
fieldLabel: 'Player 2'
}
]
}
];
this.buttons = [
{
text: 'Start Game',
action: 'start'
}
];
this.callParent(arguments);
}
});
Store
Ext.define('Darts.store.Scores', {
extend: 'Ext.data.Store',
model : 'Darts.model.Score',
autoLoad: true,
proxy: {
type: 'ajax',
api: {
read: 'data/scores.json',
update: 'data/updateScores.json'
},
reader: {
type: 'json',
root: 'scores',
successProperty: 'success'
}
}
});
Model:
Ext.define('Darts.model.Score', {
extend: 'Ext.data.Model',
fields: ['playerOne', 'playerTwo']
});
Data:
{
success: true,
scores: [
{id: 1, playerOne: '301', playerTwo: '301'}
]
}
I've tried numberfields, textfields as well as changing the data fom with ' to without ' and mixed.... nothing seems to help me.
The fields are rendered before store is loaded (test output still in the code)
I'm really out of ideas here and I've seen many topics, but none fits to my problem or fixes my problem. The form fields always keeps beeing empty.
I think your issue is that you need to pass a Model record into loadRecord method not the underlying data. So try changing line 3 to
view.down('form').loadRecord(records[0]);
As a side note, it's a bit odd to load the entire store just to get at a single record.
You might want to explore Model.load( id, {callback config} ) way of loading exact record that you need.