Related
My field looks like this:
...
{
xtype: 'gridcolumn',
text: 'MyField',
dataIndex: 'contragent',
editor: {
xtype: 'combobox',
allowBlank: false,
displayField:'name',
valueField:'id',
queryMode:'remote',
store: Ext.data.StoreManager.get('ContrAgents')
},
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
store_ca = Ext.data.StoreManager.get('ContrAgents');
if (record.data.contragent != ''){
store_ca.load();
var contr = store_ca.getById(record.data.contragent);
//indexInStore = store_ca.findExact('id', value);
console.log(contr);
return contr.data.name;
}
}
},
...
Store 'ContrAgents' looks like this:
Ext.define('BookApp.store.ContrAgents', {
extend: 'Ext.data.Store',
model: 'BookApp.model.ContrAgents',
autoLoad: true,
proxy: {
type: 'ajax',
url: 'app/data/Contragents.json'
}
});
The problem is that the name of the required field is not returned (contr.data.name), contr is null.
Apparently the store does not have time to load, in this case I need to load it, but store_ca.load () does not bring results.
How to load the store correctly to use
store_ca.getById (record.data.contragent); to return the name of the field?
I'm not entirely sure why you would need to use a store to populate the value in the grid's cell, because you could always just send the text value through the grid's store as opposed to the id.
You probably have a good reason for doing it, so I've revisited the fiddle and implemented it accordingly. You should be able to check it out here
The changes
../app/store/ContrAgents.js
Ext.define('Fiddle.store.ContrAgents', {
extend: 'Ext.data.Store',
model: 'Fiddle.model.ContrAgents',
autoLoad: false,
proxy: {
type: 'ajax',
url: 'Contragents.json'
}
});
../app/store/ListViewStore.js
Ext.define('Fiddle.store.ListViewStore', {
extend: 'Ext.data.Store',
model: 'Fiddle.model.ListViewModel',
autoLoad: false,
proxy: {
type: 'ajax',
url: 'List.json'
}
});
../app/view/ListView.js
Ext.define('Fiddle.view.ListView' ,{
extend: 'Ext.grid.Panel',
alias: 'widget.booklist',
itemId: 'BookList',
store: 'ListViewStore',
xtype: 'listview',
plugins: 'gridfilters',
initComponent: function() {
var me = this;
// Pin an instance of the store on this grid
me.myContrAgents = Ext.data.StoreManager.lookup('ContrAgents');
// Manually load the 'ContrAgents' first
me.myContrAgents.load(function(records, operation, success) {
// Now load the 'ListViewStore' store
me.getStore().load();
});
me.columns = [
{
header: 'ID',
dataIndex: 'id',
sortable: true,
width: 35
},
{
text: 'Контрагент',
dataIndex: 'contragent',
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
if (value > 0) {
if (rec = me.myContrAgents.findRecord('id', value)) {
return rec.get('name');
}
}
return '';
}
}
];
me.callParent(arguments);
}
});
Data/List.json
"data" : [
{"id": 1, "contragent": "2"},
{"id": 2, "contragent": "3"},
{"id": 3, "contragent": "4"}
]
You are trying to query the store before it's data is actually populated. You want to avoid loading the store for each time the renderer event is triggered.
The Ext.data.Store->load function is asynchronous
See docs
store.load({
scope: this,
callback: function(records, operation, success) {
// the operation object
// contains all of the details of the load operation
console.log(records);
}
});
Change your implementation to this and test if it works
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
store_ca = Ext.data.StoreManager.get('ContrAgents');
if (record.data.contragent != ''){
store_ca.load(function(records, operation, success) {
console.log('loaded records');
var contr = store_ca.getById(record.data.contragent);
indexInStore = store_ca.findExact('id', value);
console.log({
contr: contr,
indexInStore: indexInStore
});
});
// Not sure what you are trying to return here
// return contr.data.name;
}
}
Remote Combo in ExtJS Grid Example
I found a good example for what you are trying to accomplish here with a combo dropdown in a grid with a remote store, check out this post (you might have to register for free but the solution is worth it and I won't plagiarise it here)
Grid with combo edit widget with binding
Perhaps this could help...
Ext.define('Fiddle.view.ListView' ,{
extend: 'Ext.grid.Panel',
alias: 'widget.booklist',
itemId: 'BookList',
store: 'ListViewStore',
xtype: 'listview',
plugins: ['cellediting','gridfilters'],
initComponent: function() {
this.columns = [
{
header: 'ID',
dataIndex: 'id',
sortable: true,
width: 35
},
{
text: 'Контрагент',
width: 150,
xtype: 'widgetcolumn',
dataIndex: 'contragent',
widget: {
xtype: 'combo',
bind: '{record.contragent}',
allowBlank: false,
displayField: 'name',
valueField: 'id',
store: Ext.data.StoreManager.lookup('ContrAgents')
}
}
];
this.callParent(arguments);
}
});
Ext JS 5.1
I have a student management system, and 4 classes from 1th to 4th. All classes have 4 courses each. I called all courses to grid but I want to call only courses that have same class with the student. (e.g I'm on 4th class so I can see only 4th class courses on grid)
I just want to part of code like this if it is possible:
if (refs.txtClass.value === refs.lesClass.value){
// load the grid that only equals txtClass.value
// txtClass is Class No of a student, lesClass is Class No of grid
}
Student's Class Reference
items: [{
xtype: 'label',
flex: 1,
reference: 'txtClass'
}]
Grid Code
items: [
{
xtype: 'gridpanel',
flex: 1,
reference: 'courseList',
itemId: 'courseList',
store: 'CourseStore',
columns: [
{
xtype: 'gridcolumn',
reference: 'lesId',
itemId: 'lesId',
dataIndex: 'lesId',
text: 'DERS KODU'
},
{
xtype: 'gridcolumn',
reference: 'lesClass',
itemId: 'lesClass',
dataIndex: 'lesClass',
text: 'SINIF'
},
{
xtype: 'gridcolumn',
reference: 'lesName',
itemId: 'lesName',
width: 200,
dataIndex: 'lesName',
text: 'DERS ADI'
},
{
xtype: 'gridcolumn',
reference: 'lesCredit',
itemId: 'lesCredit',
dataIndex: 'lesCredit',
text: 'DERS KREDİSİ'
},
{
xtype: 'gridcolumn',
reference: 'lesTeacher',
itemId: 'lesTeacher',
width: 600,
dataIndex: 'lesTeacher',
text: 'OKUTMAN'
}
]
}]
If you have loaded all data locally , you can then filter on store based on the condition.
filterBy ( fn , [scope] )
Filters by a function. The specified function will be called for each Record in this Store. If the function returns true the Record is included, otherwise it is filtered out.
When store is filtered, most of the methods for accessing store data will be working only within the set of filtered records. The notable exception is getById.
store.filterBy(function()
{
if (refs.txtClass.value === refs.lesClass.value){
return true;
}
else {
return false;
}
},this);
You can also make use of filters config on the store.
filters: [
function(item) {
//condition here
}
]
You can also loadData method of store to load some part of data in store :
var arr = [],
store = yourgrid.getStore();
store.each(function(rec) {
if (// condition to create the part of data want to be loaded)){
arr.push(rec);
}
store.loadData(arr);
You can also used slice method to store some data on the basis of range
store.loadData(oldstoreRecords.slice(start,end);
I am looking for a tutorial / sourcecode for a sencha touch list with a model and a store. I am facing some issues with Sencha Touch 2.2.1.
Model:
Ext.define("DeviceAPIFramework.model.OfferModel", {
extend: "Ext.data.Model",
config: {
fields: [
{ name: "description", type: "string" },
{ name: "id", type: "string" }
]
}
});
Store:
Ext.define("DeviceAPIFramework.model.OfferStore", {
extend: "Ext.data.Store",
config: {
storeId: "offerStore",
model:'DeviceAPIFramework.model.OfferModel'
}
});
Controller:
offerStore.add({description: 'test', id: 'my id'});
Ext.ComponentQuery.query('#offersListHomeView')[0].update();
View:
Ext.require("DeviceAPIFramework.model.OfferStore");
var offerStore = Ext.create("DeviceAPIFramework.model.OfferStore");
Ext.define ...........
{
xtype: 'list',
width: Ext.os.deviceType == 'Phone' ? null : 1200,
height: Ext.os.deviceType == 'Phone' ? null : 350,
title: 'test',
itemId: 'offersListHomeView',
store: offerStore,
itemTpl: '{description} {id}'
}
Image:
After executing the code from the controller, a new row gets appended, but also a weird undefined text on the upper left corner of my list. Any suggestions how to fix this issue?
I also don't like the variable offerStore outside the view. If I put it in the controller, the view is nagging.
The undefined is coming from this line:
Ext.ComponentQuery.query('#offersListHomeView')[0].update();
update is decrapted(I wrote it, because I was developing once with an old Sencha Touch version and this update was necessary) and will call setHtml(), because we are passing no arguments it is settings "undefined", which will be shown in our view. In the new sencha version, you can simply delete this line.
I also managed the global store problem with this code in the controller:
launch: function() {
this.offersStore = Ext.create('Ext.data.Store', {
model: 'DeviceAPIFramework.model.OfferModel'
});
Ext.ComponentQuery.query('#offersListHomeView')[0].setStore(this.offersStore);
},
I've created a script to dynamically generate a form, but I'm having problem loading the data of the nested model. I've tried loading the whole record and I've tried loading each sub store, but neither works.
I've through about using form.load(), but from my understanding that requires a proxy connection and also require to store json data inside a 'data' array.
Does anyone have any suggestions on how might I approach this problem?
<div id="view-#pageSpecificVar" class="grid-container even"></div>
<div id="button"></div>
<script>
Ext.define('HeaderForm', {
extend: 'Ext.form.Panel',
initComponent: function () {
var me = this;
Ext.applyIf(me, {
id: Ext.id(),
defaultType: 'textfield'
});
me.callParent(arguments);
}
});
// Define our data model
Ext.define('HeaderModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'HeaderSequence', type: 'int'}
],
hasMany:[
{ name: 'Columns', model: 'ColumnModel' }
],
proxy: {
type: 'ajax',
actionMethods: { create: 'POST', read: 'GET', update: 'POST', destroy: 'POST' },
url: '#Url.Content("~/Test/Header")',
timeout: 1200000,
},
});
Ext.define('ColumnModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'ColumnWidth', type: 'float'}
],
hasMany:[
{ name: 'Fields', model: 'FieldModel'}
],
belongsTo: 'HeaderModel'
});
Ext.define('FieldModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'XType', type: 'string'},
{ name: 'FieldLabel', type: 'string'},
{ name: 'Name', type: 'string'},
{ name: 'Data', type: 'string'},
{ name: 'FieldSpecify', type: 'bool'}
],
belongsTo: 'ColumnModel'
});
var store = Ext.create('Ext.data.Store', {
storeId: 'HeaderStore',
model: 'HeaderModel',
autoDestroy: true,
listeners: {
load: function (result, records, successful, eOpts) {
//console.log(result);
var form = dynamicForm(records[0]);
form.add(submitButton);
form.render('view-#pageSpecificVar');
}
}
});
store.load();
var dynamicForm = function(record) {
var form = new HeaderForm();
var columnContainer = new Ext.widget({
xtype: 'container',
layout: 'column'
});
var formItems = new Ext.util.MixedCollection();
Ext.each(record.ColumnsStore.data.items, function(item) {
Ext.iterate(item.data, function (key, value) {
var fieldContainer = new Ext.widget({
xtype: 'container',
columnWidth: value
});
Ext.each(item.FieldsStore.data.items, function(item) {
if(item.data["FieldSpecify"]) {
fieldContainer.add(new Ext.widget({
xtype: item.data["XType"],
fieldLabel: item.data["FieldLabel"],
name: item.data["Name"],
//value: item.data["Name"]
}));
}
}, this);
columnContainer.add(fieldContainer);
}, this);
}, this);
formItems.add(columnContainer);
form.add(formItems.items);
Ext.each(record.ColumnsStore.data.items, function(item) {
Ext.each(item.FieldsStore.data.items, function(fields) {
form.loadRecord(fields);
});
});
//form.loadRecord(record);
return form;
};
var submitButton = new Ext.widget({
xtype: 'toolbar',
dock: 'bottom',
items:[{
xtype: 'button',
text: 'Save',
handler: function(button) {
var basic = button.up('form').form;
basic.updateRecord(basic.getRecord());
var store = Ext.StoreMgr.get('HeaderStore');
store.each(function(record) {
record.dirty = true;
});
store.sync();
}
}]
});
</script>
Update
Sorry I probably didn't made it very clear. I'm having problem loading the store data into form fields. For static forms I normally use loadRecord to load the nested model into a form, but in this case all the fields are nested in their own little model, so would there be a way to load each nested model value into their own field with loadRecord?
The HeaderModel stores field set information.
The purpose of ColumnModel is to create the container that will surround a set of fields, for styling purpose. It simply creates two columns of fields.
The FieldModel stores the field specific attributes and data.
Here's an example of response json data...
{
"HeaderSequence":1,
"Columns":[{
"ColumnWidth":0.5,"Fields":[
{"XType":"textfield","FieldLabel":"FieldA","Name":"NameA","Data":"A","FieldSpecify":true},
{"XType":"textfield","FieldLabel":"FieldB","Name":"NameA","Data":"B","FieldSpecify":true}]
},{
"ColumnWidth":0.5,"Fields":[
{"XType":"textfield","FieldLabel":"FieldA2","Name":"NameA2","Data":"A2","FieldSpecify":true},
{"XType":"textfield","FieldLabel":"FieldB2","Name":"NameB2","Data":"B2","FieldSpecify":true}]
}
]
}
Thanks
I've figure out how to load the nested model into the form. We can't simply use load or loadRecord, as by default that method tries to get a model's data and iterate through the data object and call setValues.
What I have to do is manually get the basic form element and call setValues myself to assign the values.
// loop through each field store to load the data into the form by field id
Ext.each(record.ColumnsStore.data.items, function(item) {
Ext.each(item.FieldsStore.data.items, function(fields) {
form.getForm().setValues([{ id: fields.data['Id'], value: fields.data['DisplayName'] }]);
});
});
To Follow up with that, a custom submit handler needs to be put in place as well.
Which loops through the store and sets the submitted value to store before sync the store.
// define form submit button
var submitButton = new Ext.widget({
xtype: 'toolbar',
dock: 'bottom',
items:[{
xtype: 'button',
text: 'Save',
handler: function(button) {
// get basic form for button
var basic = button.up('form').form;
// get form submit values
var formSubmitValues = basic.getValues();
// get header store
var store = Ext.StoreMgr.get('HeaderStore');
// loop through each field store and update the data values by id from the form
store.each(function(record) {
Ext.each(record.ColumnsStore.data.items, function(item) {
Ext.each(item.FieldsStore.data.items, function(fields) {
fields.data['Data'] = formSubmitValues[fields.data['Id']];
});
});
// mark the record as dirty to be sync
record.dirty = true;
});
// sync store object with the database
store.sync();
}
}]
});
Have a look at this and this examples on how to load nested data into a nested model. You will also see how to access the associated data.
I'm not sure why you use record.ColumnsStore.data.items, as if record is of HeaderModel type, you should really get the columns store via record.Columns, and then iterate that store.
Would also help to see what JSON your server returns.
I have a following code for combo box, how can I get the value that is selected in the combobox and load that value into a variable, and use it later.
Thank you
Ext.define('Column', {
extend: 'Ext.data.Model',
fields: ['data1', 'Data2']
});
var store = Ext.create('Ext.data.Store', {
model: 'Column',
autoLoad: true,
proxy: {
type: 'ajax',
url: '/data.xml',
reader: {
type: 'xml',
record: 'result'
}
}
});
var simpleCombo = Ext.create('Ext.form.field.ComboBox', {
store: store,
displayField: 'data1',
valueField: 'data1',
width: 250,
labelWidth: 120,
fieldLabel: 'select a value',
renderTo: 'simpleCombo',
queryMode: 'local',
typeAhead: true
});
Simply use the select event
var simpleCombo = Ext.create('Ext.form.field.ComboBox', {
store: store,
displayField: 'data1',
valueField: 'data1' ,
width: 250,
labelWidth: 120,
fieldLabel: 'select a value',
renderTo: 'simpleCombo',
queryMode: 'local',
typeAhead: true,
listeners: {
select: function(combo, records) {
// note that records are a array of records to be prepared for multiselection
// therefore use records[0] to access the selected record
}
});
API Link
Additional content from the comments:
Take a look at the multiSelect property of the combobox. You get all values separated by a defined delimiter and the select event will give you a records array with more that one record. Note the that getValue() only give you the defined displayField which is a string and not the record itself. So using iComboValue[0] gives you the first character. The selected records should always be accessed using the selected event. But you may store them in a array for later use and overwrite it with any new select.
You can also use:
var iComboValue = simpleCombo.getValue();
may be you should try this
// to get the combobox selected item outside the combo listener
simpleCombo.on('change', function (combo, record, index) {
alert(record); // to get the selected item
console.log(record); // to get the selected item
});