ExtJs: Ext.grid.Panel: Grid refreshes automatically and becomes blank - extjs

I am using a Grid to display data on a modal window.
It has two columns, 1. Label 2. TextField
The problem I am facing is whenever I enter anything in the textfield and lose focus from that textfield (by pressing TAB or clicking somewhere else), the grid clears itself completely and I get a blank grid!
I know this has something to do with the autoSync property of the Store associated with the grid.
So I set it to false autoSync: false.
After doing this the data gets retained and works fine.
BUT when I close this modal window and re-open it with the same store data, I get a blank screen!
Following is my code:
Model
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLAttributeValueModel', {
extend: 'Ext.data.Model',
fields: [
{
name: 'attribute',
type: 'string'
},
{ name: 'attributeValue',
type: 'string'
}
]
});
Store
var attrValueStore = Ext.create('Ext.data.ArrayStore', {
autoSync: true, //tried setting it to false but got error as mentioned above
model: 'Ext.ux.window.visualsqlquerybuilder.SQLAttributeValueModel'
});
GRID
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLAttributeValueGrid', {
autoRender: true,
extend: 'Ext.grid.Panel',
alias: ['widget.attributevaluegrid'],
id: 'SQLAttributeValueGrid',
store: attrValueStore,
columnLines: true,
plugins: [Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})],
columns: [
{ /*Expression */
xtype: 'gridcolumn',
text: 'Attribute',
sortable: false,
menuDisabled: true,
flex: 0.225,
dataIndex: 'attribute'
},
{ /*Attribute Values*/
xtype: 'gridcolumn',
editor: 'textfield',
text: 'Values',
flex: 0.225,
dataIndex: 'attributeValue'
}
],
initComponent: function () {
this.callParent(arguments);
}
});
MODAL WINDOW
var attributeValueForm = Ext.create('Ext.window.Window', {
title:'Missing Attribute Values',
id: 'attributeValueForm',
height:500,
width:400,
modal:true,
renderTo: Ext.getBody(),
closeAction: 'hide',
items:[
{
xtype: 'attributevaluegrid',
border: false,
//height: 80,
region: 'center',
split: true
}
],
buttons: [
{
id: 'OKBtn',
itemId: 'OKBtn',
text: 'OK',
handler: function () {
Ext.getCmp('attributeValueForm').close();
}
},
{
text: 'Cancel',
handler: function () {
Ext.getCmp('attributeValueForm').close();
}
}
]
});
Please help. This is making me go mad!!

It would be helpful if you could provide details on how you create the Window itself, as it may be part of the problem.
One cause of this can be that you are hiding the window instead of closing it and then creating a new instance when you re-open. This will cause your DOM to have two windows instances and may not sync the data correctly in the second instance.
Some more details on how you create the actual window would help shed some light on whether this is the case.

I would probably want to jail myself after writing this.
The real issue was that I had created a similar grid for a different modal window and since I had copied the code I missed out on changing the ID of the new grid.
Both grids had the same IDs.
Changed it now and it is working fine now.
Thanks for your help!

Related

Extjs checkbox shows always checked (second time onwards, within a dynamically added window modal (i.e if rendered within body only)

I have a users grid, on double click of users grid I am showing update user form in a window modal popup.
on dblClick of users grid I am adding user-detail-window
to mainView.
user-detail-window contains a user-detail form, which contains checkbox field called active
If user_abc is not active, then for the first time after opening update form for user_abc, its showing unchecked active field (this
is correct behavior, user_abc is already deactived)
If you open any other user record (which is active) and then again open user_abc (this is already deactivated), its shows active filed checked (instead of showing unchecked)
i) I have a checkbox field in user update form i.e. my UserDetail view
Ext.define('TestCMS.view.UserDetail', {
extend: 'Ext.form.Panel',
alias: 'widget.user-detail',
itemId: 'user-detail',
items: [
{
xtype: 'combobox',
name: 'locale',
fieldLabel: 'Taal',
store: 'i18n.Language',
displayField: 'iso639',
valueField: 'iso639',
autoLoad: true,
forceSelection: true,
editable: false,
triggerAction: 'all',
bind: '{currentText.locale}',
},
{
xtype: 'checkboxfield', //all fields in this form
name: 'active', //are showing proper data,
fieldLabel: 'Actief', //except this checkbox
bind: '{currentText.active}'
},
{
xtype: 'ckeditor',
fieldLabel: 'description',
itemId: 'ckeditor-body',
name: 'text',
bind: '{currentText.text}',
msgTarget: 'under',
CKConfig: {
extraPlugins: wordcount,notification',
}
}
});
ii) Following is MainView
Ext.define('TestCMS.view.UserMain', {
extend: 'Ext.container.Container',
alias: 'widget.user-main',
itemId: 'user-main',
layout: 'border',
defaults: {
split: true,
border: 0
},
viewModel: {
type: 'user-vm',
},
items: [
{
xtype: 'user-grid',
reference: 'user-grid',
region: 'west',
width: 350,
title: 'users',
multiSelect: false
}
});
iii) Following is detailWindowView
Ext.define('TestCMS.view.UserDetailWindow', {
extend: 'Ext.window.Window',
alias: 'widget.UserDetailWindow',
itemId: 'user-detail-window',
controller: 'user-detail',
requires: [
'TestCMS.controller.UserDetail',
],
layout: 'fit',
minHeight: 632,
minWidth: 1088,
closable: true,
modal: true,
closeAction: 'destroy',
maximizable: true,
bind: {
hidden: '{userDetailPanelDisabled}',
},
items: [
{
xtype: 'user-detail',
}
],
renderTo: Ext.getBody(), //This line causes the bug
});
I have added this line (i.e. renderTo: Ext.getBody()) to show Popup window on entire screen.
Without this line popup window is showing within a container only.
After adding only this line in above code, checkbox functionality related error occurred, otherwise it was working fine.
iv) Following is user gridview dblClick handler
onItemDblClick: function (view, record, item, index, e, eOpts) {
var userDetailWindow = Ext.widget('UserDetailWindow');
var mainView = view.up('#user-main');
viewModel = mainView.getViewModel();
mainView.add(userDetailWindow);
userDetailWindow.down('#user-detail').loadRecord(record);
contentWindow.show();
contentWindow.setTitle('user: ' + viewModel.data.userName);
},
Found solution after trying many different things
1) Removed Items[] from detailWindowView
items: [
{
xtype: 'user-detail',
}
],
2) Added user-detail runtime to user-detail-window
within onItemDblClick
mainView.add(userDetailWindow);
//added this line and problem solved
contentWindow.add(Ext.widget('user-detail'));
contentWindow.show();
contentWindow.setTitle('user: ' + viewModel.data.userName);

Seems like ViewModel persists after its View is destroyed in extjs

I'm sure there's something I'm missing here and I just can't see what it is.
I have demo project I'm building in extjs 6. In it I have a grid of inventory items.
Ext.define("InventoryDemo.view.inventory.list.Inventory",{
extend: "Ext.container.Container",
xtype: 'inventory',
requires: [
"InventoryDemo.view.inventory.list.InventoryController",
"InventoryDemo.view.inventory.list.InventoryModel"
],
controller: "inventory-inventory",
viewModel: {
type: "inventory-inventory"
},
closable: true,
listeners:{
refreshList: 'onRefreshList'
},
layout:{
type: 'hbox',
align: 'stretch'
},
items:[
{
xtype: 'grid',
flex: 1,
tbar:[
{xtype: 'button', text: 'New Item', handler: 'newInventoryItem'}
],
bind:{
store: '{inventory}'
},
listeners:{
itemclick: 'showDetails'
},
columns:[
{ text: 'Name', dataIndex: 'name', flex: 1 },
{ text: 'Price', dataIndex: 'price' },
{ text: 'Active', dataIndex: 'active' },
]
}
]
});
When you click on a row, a new detail panel is created and the selected record is linked to its viewmodel and it's added to the container view that holds the grid. I also want to use the same detail panel when creating a new inventory record so I extracted the shared logic for creating and editing so it can be reused in the controller.
Here's the list's controller:
Ext.define('InventoryDemo.view.inventory.list.InventoryController', {
extend: 'Ext.app.ViewController',
alias: 'controller.inventory-inventory',
config:{
// holds the newly created detail panel
detailsPanel: null
},
showDetails: function (grid, record, item, index, e, eOpts){
this.createDetailsPanel();
this.addTitleToDetailsPanel(record.get('name'));
// This creates the link in the new detail panel's viewmodel for the
// selected record. We specifically do NOT do this in the
// `newInventoryItem`.
details.getViewModel().linkTo('inventoryitem', record);
this.addDetailsPanelToView();
},
newInventoryItem: function (button, e){
this.createDetailsPanel();
this.addTitleToDetailsPanel('New Item');
// I thought that because the previous panel was destroyed during the
// `createDetailsPanel` method any previously linked record would not
// be linked to the new detail panel created and that not linking here
// would give me an empty detail panel.
this.addDetailsPanelToView();
},
createDetailsPanel: function (){
if(this.getDetailsPanel() !== null){
// I'm destroying any previous view here which, as I understand,
// would also destroy the the associated ViewController and ViewModel
// which would also kill any links to the viewmodel
this.getDetailsPanel().destroy();
}
details = Ext.create('InventoryDemo.view.inventory.details.Inventory',{
session: true,
listeners:{
refreshList: 'onRefreshList'
}
});
this.setDetailsPanel(details);
},
addDetailsPanelToView: function (){
this.getView().add(this.getDetailsPanel());
},
addTitleToDetailsPanel: function (title){
this.getDetailsPanel().setTitle("<h3>" + title + "</h3>");
},
onRefreshList: function (){
this.getViewModel().get('inventory').load();
}
});
The details panel being created looks like this:
Ext.define("InventoryDemo.view.inventory.details.Inventory",{
extend: "Ext.form.Panel",
requires: [
"InventoryDemo.view.inventory.details.InventoryController",
"InventoryDemo.view.inventory.details.InventoryModel"
],
controller: "inventory-details-inventory",
viewModel: {
type: "inventory-details-inventory"
},
flex: 1,
closable: true,
bodyPadding: 10,
reference: 'inventorydetails',
defaults:{
layout: 'anchor',
anchor: '50%'
},
dockedItems:[
{
xtype: 'toolbar',
dock: 'bottom',
ui: 'footer',
items:[
{xtype: 'button', text: 'Update', handler: 'updateRecord'},
{xtype: 'button', text: 'Delete', handler: 'deleteRecord'}
]
}
],
items:[
{
xtype: 'hiddenfield',
name: '_method',
value: 'PUT'
},
{
xtype: 'fieldset',
title: 'IDs',
collapsible: true,
defaults:{
xtype: 'textfield'
},
items:[
{
name: 'id',
fieldLabel: 'ID',
readOnly: true,
bind: '{inventoryitem.id}'
},
{
name: 'brand_id',
fieldLabel: 'Brand ID',
readOnly: true,
bind: '{inventoryitem.brand_id}'
}
]
},
{
xtype: 'fieldset',
title: 'Details',
defaults:{
xtype: 'textfield'
},
items:[
{
name: 'name',
fieldLabel: 'Name',
bind: '{inventoryitem.name}'
},
{
name: 'price',
fieldLabel: 'Price',
bind: '{inventoryitem.price}'
}
]
}
]
});
The problem that I'm running into is if I click on a row to view it's details (which works) and then click on the New Item button, the record that was loaded on the previous detail panel is still loaded on the new detail panel.
If I click the New Item button first I get the blank form I'm going for and if I select different item rows each of the records from the selected row loads into the detail panel correctly (it's not a situation where the record from the first row is "stuck" in the detail panel), but as soon as I select a row, the New Item button will only give me forms with the previously loaded record.
Is there something that would make a link to a viewmodel persist between the destruction and creation of two separate views/viewmodels/viewcontrollers (or is there a flaw in my controller logic that I'm just not seeing)?
if the only thing you need in the detail's viewModel is this singular property you are trying to link to, consider not using a stand-alone viewModel for the detail at all.
When your detail panel resides in the items of your inventory view, it actually has natural access to the view's viewModel (detailPanel.lookupViewModel() will return the single closest viewModel in the component tree hierarchy). Bindings should work too.
However, if you need a separate viewModel for the detail panel, you can create the detail view with an ad-hoc viewModel config that gets merged into the detail's viewModel upon instantiation.
The view:
Ext.define('APP.view.TheView',{
extend: "Ext.container.Container",
alias: 'widget.theView',
controller: 'theView',
viewModel: { type: 'theView' },
config: {
detailConfig: { xtype: 'theDetail', reference: 'detail' }
},
items: ...
};
The view's viewController:
Ext.define('APP.controller.TheView',{
extend: "Ext.app.ViewController",
alias: 'controller.theView',
onOpenDetail: function(){
var detail = this.lookupReference('detail');
if(!detail){
var view = this.getView(),
detailConfig = view.getDetailConfig(),
theProperty = this.getViewModel().get('theProperty');
detailConfig = Ext.apply({
viewModel: {
// This actually gets correctly merged with whatever there is
// in the viewModel configuration of the detail
data: { theProperty: theProperty }
}
}, detailConfig));
detail = view.add(detailConfig);
}
// Do something with the detail instance
}
};
And the detail:
Ext.define('APP.view.TheDetail',{
extend: "Ext.panel.Panel",
alias: 'widget.theDetail',
controller: 'theDetail',
viewModel: { type: 'theDetail' },
items: ...
};
Hope this helps a little! :-)

ExtJS grid combo renderer not working

I have a grid in my ExtJS 4.2.1 application that has an editable column with combo editor.
I need to render the column with the value from the DisplayField of the combo but the comboStore.getCount() = 0
Here is my grid:
Ext.define('App.employee.Grid', {
extend: 'Ext.grid.Panel',
requires: ['Ext.grid.plugin.CellEditing'],
alias: 'widget.employee.grid',
config: {
LocationId: 0
},
initComponent: function() {
var me = this,
store = me.buildStore(),
comboStore = Ext.create('App.store.catalog.Location', { autoLoad: true });
me.rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
autoCancel: false,
listeners: {
edit: function(editor, e) {
}
}
});
me.cellEditing = new Ext.grid.plugin.CellEditing({
clicksToEdit: 1
});
Ext.applyIf(me, {
plugins: [me.rowEditing],
columns: [{
xtype: 'rownumberer',
text: '#',
width: 50,
sortable: false,
align: 'center'
//locked: true
},{
text: 'Number',
dataIndex: 'EmployeeNumber',
align: 'center',
width: 90
}, {
text: 'Name',
dataIndex: 'EmployeeName',
flex: 1
}, {
text: 'Location',
dataIndex: 'LocationId',
width: 140,
renderer: function(value) {
// HERE!!!
// me.comboStore.getCount() = 0 so I never get a record
var record = me.comboStore.findRecord('LocationId', value);
return record.get('Description');
},
editor: {
xtype: 'combobox',
typeAhead: true,
triggerAction: 'all',
store: comboStore,
displayField: 'Description',
valueField: 'LocationId',
queryMode: 'local',
listConfig: {
width: 250,
// Custom rendering template for each item
getInnerTpl: function() {
return '<b>{Code}</b><br/>(<span style="font-size:0.8em;">{Description}</span>)';
}
}
}
}],
store: store,
});
me.callParent(arguments);
}
});
The problem is in the renderer function because the comboStore is always empty. The strange thing is that in my view If I click to edit the row and open the combo the combo has values.
[UPDATE]
What I think is that my comboStore has a delay when loading so the renderer is fired before the comboStore gets loaded. I figure this out because if I debug in chrome and I wait a few seconds, then it works... but don't know how to force to wait until comboStore is loaded.
Any clue on how to solve this? Appreciate any help.
A couple of solutions:
Ensure that the combo store is loaded before the grid store. This can be done by loading the combo first and from load event of its store trigger the grid store load. The disadvantage is that it adds unnecessary delay so this method impairs the user experience.
Load all needed stores in one request. It requires a bit of coding but it saves server roundtrip so it's a very valuable approach. Store loadData method is used to actually load store with the received data. You would, of course, first call it on combo, then on the grid.
The best method that I use almost exclusively is to turn the whole thing upside down and link display field to the store, not value field. Server must return both display and value fields in the grid store and a little piece of code that updates both fields in the grid store after editing is complete is required. This method is demonstrated here: Remote Combo in ExtJS Grid

Popup with Grid as well as submit button using Extjs

In our application we are using Extjs. Now I need a pop up with a grid and a cancel and submit button. So that I can select some records from the grid and save the record to DB.
I tried Ext.Window for popup.
I think items attribute in the Ext.Window can hold only one type of object ( means the object of Ext.grid.GridPanel or form). But I need both controls.
How can I implement both controls with in a popup window?
Please give your valueable information about this.
Thanks in advance.
Given you're code submitted in the comments (btw, you can edit your question to include in the question).
You can either add multiple objects to the items array or, in this case, I would add a buttons bar to the bottom (bbar)
Here is code demonstrating this, Additionally here is working fiddle:
var myData = [
['ddd', '1111'],
['eee', '2222']
];
var store = new Ext.data.ArrayStore({
fields: [{
name: 'FLD',
type: 'string'
}, {
name: 'VAL',
type: 'string'
}]
});
store.loadData(myData);
var grid = new Ext.grid.GridPanel({
store: store,
loadMask: true,
//renderTo:Ext.getBody(),
columns: [{
header: 'FLD',
dataIndex: 'FLD'
}, {
header: 'VAL',
dataIndex: 'VAL'
}],
viewConfig: {
forceFit: true
}
});
var window = Ext.create('Ext.window.Window', {
title: 'My Title',
height: 400,
width: 600,
items: [
grid
],
bbar: [{
text: 'Save',
handler: function(btn) {
alert('Save!');
}
}, {
text: 'Cancel',
handler: function(btn) {
alert('Cancel!');
}
}]
});
window.show();

Extjs 4.1 - Listerning in CellEditing plugin not working at second time

i define a treeGrid with plugin CellEditing like
Ext.define('MyExample', {
extend: 'Ext.tree.Panel',
id: 'example',
alias: 'example',
....
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1,
listeners: {
beforeedit: function(plugin, edit){
alert('don't run second time');
}
}
})
],
...
And i have a button when i click this button will call below window (this window has treeGrid above)
Ext.create('Ext.window.Window', {
title: 'Phân xử lý',
modal:true,
height: 500
width: 500
layout: 'border',
...
item[
{
title: 'example',
region: 'center',
xtype: 'example', // that here
layout: 'fit'
}
]
Everything working at first time but when i close the window at first time and click button to call window again then CellEditting still working but listeners not working ?
How to fix that thanks
EDIT
Please see my example code in http://jsfiddle.net/E6Uss/
In first time when i click button. Everything working well like
But when i close Example window and open it again I try click to blocked cell again i get a bug like
How to fix this bug? thanks
The problem here is that you are using Ext.create inside the plugins array for the tree grid. This have the effect of creating it once and attaching the result adhoc to your defined class.
If you close the window, all the resources within the window are destroyed. The second time you instantiate the tree panel, the plugin is not there. Take a look at this fiddle : I see what your issue is. Take a look at http://jsfiddle.net/jdflores/E6Uss/1/
{
ptype : 'cellediting',
clicksToEdit: 1,
listeners: {
beforeedit: function(plugin, edit){
console.log('EDITOR');
if (edit.record.get('block')) {
alert('this cell have been blocked');
return false;
}
}
}
}
You're recreating the window on every click of the button. This recreation may be messing with your configuration objects or destroying associations or references within them, or something similar. Try to reuse the window everytime by replacing your button code with something like:
Ext.create('Ext.Button', {
text: 'Click me',
visible: false,
renderTo: Ext.getBody(),
handler: function(button) {
if(!button.myWindow)
{
button.myWindow = Ext.create('Ext.window.Window', {
title: 'Example',
height : 300,
width : 500,
layout: 'border',
closeAction: 'hide',
items: [{
region: 'center',
floatable:false,
layout:'fit',
xtype: 'example',
margins:'1 1 1 1'
}
]
});
}
button.myWindow.show();
}
});
Maybe it needed complete the editing cell, try finish it:
...
beforeedit: function(plugin, edit){
alert('don't run second time');
plugin.completeEdit();
}
Sencha: CompleteEdit
I put together a working example here: http://jsfiddle.net/jdflores/6vJZf/1/
I think the problem with your column is that it's dataIndex is expecting that the editor value be a boolean type (because 'block' was set as datIndex instead of 'date'). I assume you are using the 'block' field just to identify which are blocked to be edited. I modified the fidle provided here: http://jsfiddle.net/jdflores/6vJZf/1/
{
text: 'Block Date',
dataIndex: 'date',
xtype: 'datecolumn',
format: 'd/m/Y',
editor: {
xtype: 'datefield',
allowBlank: true,
format: 'd/m/Y'
}
}

Resources