Seems like ViewModel persists after its View is destroyed in extjs - 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! :-)

Related

Extjs textfield binding

I am trying to find the best way to bind items to a textfield in my extjs project. I downloaded the data into a store with one record in the controller. How would I bind to this textfield from the one record? I would preferably bind in the view, not in the controller
You should read this guide to understand better what binding is
The best solution for you is bind the record on the viewmodel of the view, so:
textfield.setBind({
value:'{myRec.valueToRefer}'
})
viewmodel.set('myRec',record.getData());
If you want, you can also use a form to handle this, using form.loadRecord and giving to the textfield a name..
A tip:
set inside the viewmodel a value to null:
data:{
myRec:null
}
Set your record in the viewmodel after setting the bind to the textfield.
Other tip:
If you can, avoid using setBind and prefer to set the binding directly on textfield creation:
//WILL WORK BUT YOU CAN AVOID IT
textfield.setBind({
value:'{myRec.valueToBind}'
})
//YES
var textfield=Ext.create({
xtype:'textfield',
bind:{
value:'{myRec.valueToBind}'
}
});
Refer to Sencha documentation also
You can use bind config to bind the data or any other config for ExtJS component.
Bind setting this config option adds or removes data bindings for other configs.
For example, to bind the title config:
var panel = Ext.create({
xtype: 'panel',
bind: {
title: 'Hello {user.name}'
}
});
To dynamically add bindings:
panel.setBind({
title: 'Greetings {user.name}!'
});
To remove bindings:
panel.setBind({
title: null
});
In this FIDDLE, I have created a demo for biding. I hope this will help/guide you to achieve you requirement.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
//defining Store
Ext.define('Store', {
extend: 'Ext.data.Store',
alias: 'store.gridstore',
autoLoad: true,
fields: ['name', 'email', 'phone'],
proxy: {
type: 'ajax',
url: 'data1.json',
reader: {
type: 'json',
rootProperty: ''
}
}
});
//defining view model
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myvm',
data: {
formdata: null
},
stores: {
gridstore: {
type: 'gridstore'
}
}
});
//Controller
Ext.define('MyViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.myview',
onGridItemClick: function (grid, record) {
//Bind the form data for CLICKED record
this.getViewModel().set('formdata', record)
}
});
//creating panel with GRID and FORM
Ext.create({
xtype: 'panel',
controller: 'myview',
title: 'Binding Example',
renderTo: Ext.getBody(),
viewModel: {
type: 'myvm'
},
layout: 'vbox',
items: [{
xtype: 'grid',
flex: 1,
width: '100%',
bind: '{gridstore}',
columns: [{
text: 'Name',
dataIndex: 'name'
}, {
text: 'Email',
dataIndex: 'email',
flex: 1
}, {
text: 'Phone',
dataIndex: 'phone'
}],
listeners: {
itemclick: 'onGridItemClick'
}
}, {
xtype: 'form',
flex: 1,
width: '100%',
defaults: {
anchor: '100%'
},
title: 'Bind this form on Grid item Click',
bodyPadding:15,
margin: '20 0 0 0',
// The fields
defaultType: 'textfield',
items: [{
fieldLabel: 'Name',
name: 'first',
allowBlank: false,
bind: '{formdata.name}'
}, {
fieldLabel: 'Email',
name: 'email',
allowBlank: false,
bind: '{formdata.email}'
}, {
fieldLabel: 'Phone',
name: 'phone',
allowBlank: false,
bind: '{formdata.phone}'
}]
}]
});
}
});

ExtJS Use same window for editing and adding a record

I have my ExtJS 4.2.1 MVC Application.
Inside my app, I have a grid with a toolbar. The toolbar has 2 buttons, "Add New" and "Edit Record".
Inside my Controller.js I listen for my toolbar buttons click events.
this.control({
'toolbar #newRecord': {
click: this.addRecord
},
'toolbar #editRecord': {
click: this.editRecord
},
'[xtype=editshiftcode] button[action=commit]': {
click: this.saveRecord // here I have to add or edit
}
}
Then I have Window that I want to use for editing and adding records:
Ext.define('App.view.EditShiftCode', {
extend: 'Ext.window.Window',
alias: 'widget.editshiftcode',
height: 250,
width: 550,
title: 'Add / Edit Shift',
modal: true,
resizable: false,
layout: 'fit',
glyph: Glyphs.PENCIL,
initComponent: function() {
var me = this;
Ext.applyIf(me, {
items: [{
xtype: 'container',
//height: 175,
layout: {
type: 'hbox',
align: 'strech'
},
items: [{
xtype: 'form',
bodyPadding: 10,
autoScroll: false,
itemId: 'editForm',
defaults: { // defaults are applied to items, not the container
allowBlank: false,
allowOnlyWhitespace: false,
msgTarget: 'side',
xtype: 'textfield'
},
items: [
{
xtype: 'textfield',
name: 'ShiftCode',
fieldLabel: 'Code'
},
{
xtype: 'textfield',
name: 'Description',
fieldLabel: 'Description'
},
{
xtype: 'hiddenfield',
name: 'ShiftType'
},
{
xtype: 'hiddenfield',
name: 'ShiftTypeId'
},
{
xtype: 'textfield',
name: 'Hours',
fieldLabel: 'Hours per Day'
},
{
xtype: 'colorcombo',
name: 'ColorHex',
fieldLabel: 'Color'
},
{
xtype: 'checkbox',
fieldLabel: 'Active',
name: 'IsActive',
}
],
}]
}],
buttons: [
{
text: 'OK',
action: 'commit',
glyph: Glyphs.SAVE
},
{
text: 'Cancel',
action: 'reject',
glyph: Glyphs.CANCEL
}]
});
me.callParent(arguments);
},
});
My editRecord function is:
editRecord: function (button, e, options) {
var me = this;
var window = Ext.create('App.view.EditShiftCode');
window.show();
},
My addRecord function is:
addRecord: function (button, e, options) {
var me = this;
var window = Ext.create('App.view.EditShiftCode');
window.show();
},
My saveRecord function is:
saveRecord: function (button, e, options) {
// here i need to know what operation im going to perform.
// if Im going to edit then I have to update my form record to my store record
// if im going to add then I need to add a new item to my store.
}
Is this correct? To use the same function to perform 2 different actions? And if so, how can I know what action am I going to perform and how to do it?
Thanks
This way always worked for me:
User selects a record in the grid
User clicks "Edit record"
I use loadRecord to load the selected record in the form
When he clicks OK I call updateRecord
If user clicks "Add record" I create a new blank record and go to step 3
If he clicks OK after adding record, I just add the new record to the grid - you can easily know if the record is new or existing by checking phantom flag. So if record.phantom === true then it is new.

ExtJS 4.2 - Bind Date Picker to Grid - Newbie Q

I am new to ExtJS 4 and struggling at times with the learning curve. I have followed the documentation on sencha's site for MVC concept for basic structure of my app, however I am having difficulty determining where/how to implement certain components/handlers/listeners as I don't quite have the feel for this frame work yet.
So, here is my question.... (Yes I did look at other posts on SO but I think at this point I am too stupid to identify and apply what similar posters may have come accross to solve my issues)
How do I bind a date field in my grid to the date picker date that is selected and vice versa? If I select a date in my date picker I would like to have my grid load relevant rows from my db. If I select a row in my grid I would like to see the date picker reflect the date in the selected row.
Can someone give me a narrative of the approach i should be taking? I have seen some code examples but I don't clearly see an obvious preferred method or the way it should be done. If there is a link someone can give me to look at I will be happy to study.
This is my first post on SO so please forgive me for any etiquette I am lacking as well as other annoying things. Thanks in advance!
Store:
Ext.define('AM.store.Users', {
extend: 'Ext.data.Store',
model: 'AM.model.User',
autoLoad: true,
autoSync:true,
pageSize:50,
proxy:
{
type: 'ajax',
api:
{
read: 'http://192.168.0.103/testit/dao_2.cfc?method=getContent',
update: 'http://192.168.0.103/testit/dao_2-post.cfc?method=postContent'
},
reader:
{
type: 'json',
root: 'data',
successProperty: 'success',
totalProperty : 'dataset'
}}
});
model:
Ext.define('AM.model.User', {
extend: 'Ext.data.Model',
fields: [
{name: 'message_id',type: 'textfield'},
{name: 'recip_email',type: 'textfield'},
{name: 'unix_time_stamp',type:'datefield'}
]
});
View:
Ext.define('AM.view.user.List' ,{
extend: 'Ext.grid.Panel',
alias: 'widget.userlist',
title: 'All Users',
store: 'Users',
plugins:[Ext.create('Ext.grid.plugin.RowEditing', {clicksToEdit: 1})],
dockedItems: [{ xtype: 'pagingtoolbar',
store: 'Users',
dock: 'bottom',
displayMsg: 'Displaying Records {0} - {1} of {2}',
displayInfo: true}],
initComponent: function() {
this.columns = [
Ext.create('Ext.grid.RowNumberer',
{
resizable: true,
resizeHandles:'all',
align: 'center',
minWidth: 35,
maxWidth:50
}),
{
header: 'Name',
dataIndex: 'message_id',
flex: 1,
editor:'textfield',
allowBlank: false,
menuDisabled:true
},
{
header: 'Email',
dataIndex: 'recip_email',
flex: 1,
editor:'textfield',
allowBlank: false,
menuDisabled:true
},
{
header: 'Date Time',
dataIndex: 'unix_time_stamp',
width: 120,
menuDisabled:true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
field:{ xtype:'datefield',
autoSync:true,
allowBlank:false,
editor: new Ext.form.DateField(
{format: 'm/d/y'}) }
}];
this.callParent(arguments);
},
});
Viewport:
Ext.Loader.setConfig({enabled:true});
// This array is for testing.
dateArray = ["12/14/2013","12/16/2013","12/18/2013","12/20/2013"];
Ext.application({
requires: ['Ext.container.Viewport'],
name: 'AM',
appFolder: 'app',
controllers: ['Users'],
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'border',
items:
[
{
region: 'center',
//layout:'fit',
title:'The Title',
xtype: 'tabpanel', // TabPanel itself has no title
activeTab: 0, // First tab active by default
items:
[{
xtype: 'userlist',
listeners:
{
select: function(selModel, record, index, options)
{
// do something with the selected date
// Ext.Msg.alert(record.data.message_id, record.data.recip_email +'<br> ' + record.data.unix_time_stamp);
}
}
}]
},
{
region: 'west',
layout:'fit',
xtype: 'tabpanel',
activetab:0,
collapsible:false,
split: false,
title: 'The Title',
width:178,
maxWidth:400,
height: 100,
minHeight: 100,
items:
[
{
title: 'Tab 1',
xtype:'panel',
items:
[{
xtype: 'datepicker',
title: 'mydate',
minDate: new Date('12/15/2013'),
maxDate: new Date(),
// Disable dates is set to invert dates in array
disabledDates:["^(?!"+dateArray.join("|")+").*$"],
// disabledDates:["^("+dateArray.join("|")+").*$"],
handler: function(picker, date)
{
// do something with the selected date
Ext.Msg.alert('date picker example in init2.js');
}
}]
},
{
title: 'Tab 2',
html: 'ers may be added dynamically - Others may be added dynamically',
}
]
}
]
});
}
});
Update to Datepicker in Viewport:
One additional note is that i notice a property attribute in the JSON packet that has the date included even without making you suggested changes to the store. I notice there may be a bug in the link you provided?? If i set to false or remove it altogether from my store it has same behavior and is included in my JSON packet.
Do I need to encode the url also? when I click on a row in my grid and hit the update button i recive the grid row on my server side with what appears to be already url encoded by extjs perhaps?
Ext.Loader.setConfig({enabled:true});
// This array is for testing.
dateArray = ["12/14/2013","12/16/2013","12/18/2013","12/20/2013"];
Ext.application({
requires: ['Ext.container.Viewport'],
name: 'AM',
appFolder: 'app',
controllers: ['Users'],
launch: function() {
Ext.create('Ext.container.Viewport', {
layout: 'border',
items:
[
{
region: 'center',
//layout:'fit',
title:'The Title',
xtype: 'tabpanel', // TabPanel itself has no title
activeTab: 0, // First tab active by default
items:
[{
xtype: 'userlist',
listeners:
{
select: function(selModel, record, index, options)
{
// do something with the selected date
// Ext.Msg.alert(record.data.message_id, record.data.recip_email +'<br> ' + record.data.unix_time_stamp);
}
}
}]
},
{
region: 'west',
layout:'fit',
xtype: 'tabpanel',
activetab:0,
collapsible:false,
split: false,
title: 'The Title',
width:178,
maxWidth:400,
height: 100,
minHeight: 100,
items:
[
{
title: 'Tab 1',
xtype:'panel',
items:
[{
xtype: 'datepicker',
minDate: new Date('12/15/2013'),
maxDate: new Date(),
// Disable dates is set to invert dates in array
disabledDates:["^(?!"+dateArray.join("|")+").*$"],
// disabledDates:["^("+dateArray.join("|")+").*$"],
handler: function(picker, date)
{
// do something with the selected date
// Ext.Msg.alert('date picker example in init2.js' + '<br>' + Ext.Date.format(date,'m/d/Y'));
console.log('date picker example in init2.js' + Ext.Date.format(date,'m/d/Y'));
// get store by unique storeId
var store = Ext.getStore('Users');
// clear current filters
store.clearFilter(true);
// filter store
store.filter("unix_time_stamp", Ext.Date.format(date,'m/d/Y'));
// store.proxy.extraParams = { key:'test'};
store.load();
}
}]
},
{
title: 'Tab 2',
html: 'ers may be added dynamically - Others may be added dynamically',
}
]
}
]
});
}
});
If you want to filter records displayed in grid by selected date in date picker on the server side try to filter grid's store.
In your store configuration you need to set remoteFilter config attribute to true. Then store proxy will automatically add filter params into store load data requests. Also if you have only one instance of this store, add to configuration unique storeId.
In your datepicker handler you need set store filter to selected date:
handler: function(picker, date)
{
// get store by unique storeId
var store = Ext.getStore('storeId');
// clear current filters
store.clearFilter(true);
// filter store
store.filter("unix_time_stamp", date);
}
Then on server side you need to parse and process filter param.

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

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!

ext js multiple instances of same grid

I'm having an issue with multiple instances of an ext js grid all showing the same data. I am using Ext js 4.1.1.
I have a main tab panel. In that panel, there are multiple people tabs. Inside each person tab is a details tab and a family tab.
The details tab is a simple form with text boxes, combo boxes, etc. The family tab has both a dataview and a grid.
If only one person tab is open at a time, everything works fine. As soon as a second person is opened, the family tabs look the same (both the dataview and the grid). It seems to me that the problem has something to do with the model. Perhaps they are sharing the same instance of the model, and that is causing one refresh to change all the data. The dataview and the grid both have the same problem, but I think that if I can fix the problem with the grid, then I can apply the same logic to fix the dataview. I will leave the code for the dataview out of this question unless it becomes relevant.
PersonTab.js
Ext.require('Client.view.MainTab.PersonDetailsForm');
Ext.require('Client.view.MainTab.PersonFamilyForm');
Ext.require('Client.view.MainTab.EventForm');
Ext.define('Client.view.MainTab.PersonTab',
{
extend: 'Ext.tab.Panel',
waitMsgTarget: true,
alias: 'widget.MainTabPersonTab',
layout: 'fit',
activeTab: 0,
tabPosition: 'bottom',
items:
[
{
title: 'Details',
closable: false,
xtype: 'MainTabPersonDetailsForm'
},
{
title: 'Family',
closable: false,
xtype: 'MainTabPersonFamilyForm'
},
{
title: 'page 3',
closable: false,
xtype: 'MainTabEventForm'
}
]
});
MainTabPersonFamilyForm.js
Ext.require('Client.view.MainTab.PersonFamilyHeadOfHouseholdDataView');
Ext.require('Client.view.MainTab.PersonFamilyGrid');
Ext.define('Client.view.MainTab.PersonFamilyForm',
{
extend: 'Ext.form.Panel',
alias: 'widget.MainTabPersonFamilyForm',
waitMsgTarget: true,
padding: '5 0 0 0',
autoScroll: true,
items:
[
{
xtype: 'displayfield',
name: 'HeadOfHouseholdLabel',
value: 'The head of my household is:'
},
{
xtype: 'MainTabPersonFamilyHeadOfHouseholdDataView'
},
{
xtype: 'checkboxfield',
boxLabel: "Use my Head of Household's address as my address",
boxLabelAlign: 'after',
inputValue: true,
name: 'UseHeadOfHouseholdAddress',
allowBlank: true,
padding: '0 20 5 0'
},
{
xtype: 'MainTabPersonFamilyGrid'
}
],
config:
{
idPerson: ''
}
});
MainTabPersonFamilyGrid.js
Ext.require('Client.store.PersonFamilyGrid');
Ext.require('Ext.ux.CheckColumn');
Ext.define('Client.view.MainTab.PersonFamilyGrid',
{
extend: 'Ext.grid.Panel',
alias: 'widget.MainTabPersonFamilyGrid',
waitMsgTarget: true,
padding: '5 0 0 0',
xtype: 'grid',
title: 'My Family Members',
store: Ext.create('Client.store.PersonFamilyGrid'),
plugins: Ext.create('Ext.grid.plugin.CellEditing'),
viewConfig:
{
plugins:
{
ptype: 'gridviewdragdrop',
dragGroup: 'PersonFamilyGridTrash'
}
},
columns:
[
{ text: 'Name', dataIndex: 'Name'},
{ text: 'Relationship', dataIndex: 'Relationship', editor: { xtype: 'combobox', allowblank: true, displayField: 'display', valueField: 'value', editable: false, store: Ext.create('Client.store.Gender') }},
{ xtype: 'checkcolumn', text: 'Is My Guardian', dataIndex: 'IsMyGuardian', editor: { xtype: 'checkboxfield', allowBlank: true, inputValue: true }},
{ xtype: 'checkcolumn', text: 'I Am Guardian', dataIndex: 'IAmGuardian', editor: { xtype: 'checkboxfield', allowBlank: true, inputValue: true } }
],
height: 200,
width: 400,
buttons:
[
{
xtype: 'button',
cls: 'trash-btn',
iconCls: 'trash-icon-large',
width: 64,
height: 64,
action: 'trash'
}
]
});
PersonFamilyGrid.js (store)
Ext.require('Client.model.PersonFamilyGrid');
Ext.define('Client.store.PersonFamilyGrid',
{
extend: 'Ext.data.Store',
autoLoad: false,
model: 'Client.model.PersonFamilyGrid',
proxy:
{
type: 'ajax',
url: '/Person/GetFamily',
reader:
{
type: 'json'
}
}
});
PersonFamilyGrid.js (model)
Ext.define('Client.model.PersonFamilyGrid',
{
extend: 'Ext.data.Model',
fields:
[
'idFamily',
'idPerson',
'idFamilyMember',
'Name',
'Relationship',
'IsMyGuardian',
'IAmGuardian'
]
});
relevant code from the controller:
....
....
var personTab = thisController.getMainTabPanel().add({
xtype: 'MainTabPersonTab',
title: dropData.data['Title'],
closable: true,
layout: 'fit',
tabpanelid: dropData.data['ID'],
tabpaneltype: dropData.data['Type']
});
personTab.items.items[0].idPerson = dropData.data['ID'];
personTab.items.items[1].idPerson = dropData.data['ID'];
thisController.getMainTabPanel().setActiveTab(personTab);
....
....
You're setting the store as a property on your grid prototype and creating it once at class definition time. That means that all your grids instantiated from that class will share the exact same store.
Note that you're also creating a single cellediting plugin that will be shared with all instantiations of that grid as well. That definitely won't work. You likely will only be able to edit in either the first or last grid that was instantiated.
In general you should not be setting properties like store, plugins, viewConfig or columns on the class prototype. Instead you should override initComponent and set them inside that method so that each instantiation of your grid gets a unique copy of those properties.
Ext.define('Client.view.MainTab.PersonFamilyGrid', {
extend: 'Ext.grid.Panel',
alias: 'widget.MainTabPersonFamilyGrid',
waitMsgTarget: true,
padding: '5 0 0 0',
title: 'My Family Members',
height: 200,
width: 400
initComponent: function() {
Ext.apply(this, {
// Each grid will create its own store now when it is initialized.
store: Ext.create('Client.store.PersonFamilyGrid'),
// you may need to add the plugin in the config for this
// grid
plugins: Ext.create('Ext.grid.plugin.CellEditing'),
viewConfig: {
plugins: {
ptype: 'gridviewdragdrop',
dragGroup: 'PersonFamilyGridTrash'
}
},
columns: /* ... */
});
this.callParent(arguments);
}
});
It's hard to tell exactly, but from the code you have submitted it appears that you are not setting the id parameter on your tabs and your stores, which causes DOM collisions as the id is used to make a component globally unique. This has caused me grief in the past when sub-classing components (such as tabs and stores) and using multiple instances of those classes.
Try giving each one a unique identifier (such as the person id) and then referencing them using that id:
var personTab = thisController.getMainTabPanel().add({
id: 'cmp-person-id',
xtype: 'MainTabPersonTab',
...
store: Ext.create('Client.store.PersonFamilyGrid',
{
id: 'store-person-id',
...
});
Ext.getCmp('cmp-person-id');
Ext.StoreManager.lookup('store-person-id');
Hope that helps.

Resources