ExtJS 4 - Reuse components in MVC architecture
I have a list of users and if I click on an item in this list, a window opens. This is the same window for each user, and it's possible to have several window open at the same time. The window shows user informations so for this components I have the same store and the same model.
But if I load data in a specific window, I load the same data in all other open windows.
Ext.define('Cc.view.absence.Grid', {
extend: 'Ext.grid.Panel',
alias: 'widget.absencegrid',
border:false,
initComponent: function() {
Ext.apply(this, {
store: Ext.create('Ext.data.Store', {
model: 'Cc.model.Absence',
autoLoad: false,
proxy: {
type: 'ajax',
reader: {
type: 'json'
}
}
}),
columns: [
{header: 'Du', dataIndex: 'startdate', flex: 3, renderer: this.formatDate},
{header: 'Au', dataIndex: 'enddate', flex: 3, renderer: this.formatDate},
{header: 'Exercice', dataIndex: 'year', align: 'center', flex: 1},
{header: 'Statut', xtype:'templatecolumn', tpl:'<img src="../images/status-{statusid}.png" alt="{status}" title="{status}" />', align: 'center', flex: 1},
{header: 'Type d\'absence', dataIndex: 'absencetype', align: 'center', flex: 2},
{header: 'Commentaires', dataIndex: 'comment', flex: 6}
],
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
items: [
{ xtype: 'tbfill'},
{ xtype: 'button', text: 'Rafraichir', action: 'refresh', iconCls: 'item-rafraichir' }
]
}]
});
this.callParent(arguments);
},
formatDate: function(date) {
if (!date) {
return '';
}
return Ext.Date.format(date, 'l d F Y - H:i');
}
});
my controller :
Ext.define('Cc.controller.Absences', {
extend: 'Ext.app.Controller',
models: ['Absence', 'AbsenceHistory'],
views: [],
refs: [
{ ref: 'navigation', selector: 'navigation' },
{ ref: 'tabPanel', selector: 'tabpanel' },
{ ref: 'absencePanel', selector: 'absencepanel' },
{ ref: 'refreshButton', selector: 'absencepanel button[action=refresh]'},
{ ref: 'absenceGrid', selector: 'absencegrid' },
{ ref: 'absenceHistory', selector: 'absencehistory' },
],
init: function() {
this.control({
'absencepanel button[action=refresh]': {
click: this.onClickRefreshButton
},
'absencegrid': {
selectionchange: this.viewHistory
},
'absencegrid > tableview': {
refresh: this.selectAbsence
},
});
},
selectAbsence: function(view) {
var first = this.getAbsenceGrid().getStore().getAt(0);
if (first) {
view.getSelectionModel().select(first);
}
},
viewHistory: function(grid, absences) {
var absence = absences[0],
store = this.getAbsenceHistory().getGrid().getStore();
if(absence.get('id')){
store.getProxy().url = '/absence/' + absence.get('id') +'/history';
store.load();
}
},
onClickRefreshButton: function(view, record, item, index, e){
var store = this.getAbsenceGrid().getStore();
store.load();
},
});
other controller which create only one instance of absence.Panel :
Ext.define('Cc.controller.Tools', {
extend: 'Ext.app.Controller',
stores: ['Tools', 'User' ],
models: ['Tool'],
views: [],
refs: [
{ ref: 'navigation', selector: 'navigation' },
{ ref: 'tabPanel', selector: 'tabpanel' },
{ ref: 'toolList', selector: 'toollist' },
{ ref: 'toolData', selector: 'toollist dataview' }
],
init: function() {
this.control({
'toollist dataview': {
itemclick: this.loadTab
},
});
},
onLaunch: function() {
var dataview = this.getToolData(),
store = this.getT开发者_高级运维oolsStore();
dataview.bindStore(store);
},
loadTab: function(view, record, item, index, e){
var tabPanel = this.getTabPanel();
switch (record.get('tab')) {
case 'absences':
if(Ext.getCmp('absence-panel')){
tabPanel.setActiveTab(Ext.getCmp('absence-panel'));
}
else {
var panel = Ext.create('Cc.view.absence.Panel',{
id: 'absence-panel'
}),
store = panel.getGrid().getStore();
panel.enable();
tabPanel.add(panel);
tabPanel.setActiveTab(panel);
store.getProxy().url = '/person/' + this.getUserId() +'/absences';
store.load();
}
break;
default:
break;
}
},
getUserId: function(){
var userStore = this.getUserStore();
var id = userStore.first().get('id')
return id;
}
});
the other controller which create many instances of absence.Panel :
Ext.define('Cc.controller.Agents', {
extend: 'Ext.app.Controller',
stores: ['Agents'],
models: ['Agent', 'Absence', 'AbsenceHistory'],
views: ['agent.List', 'agent.Window', 'absence.Panel', 'absence.Grid', 'absence.History'],
refs: [
{ ref: 'agentList', selector: 'agentlist' },
{ ref: 'agentData', selector: 'agentlist dataview' },
{ ref: 'agentWindow', selector: 'agentwindow' },
],
init: function() {
this.control({
'agentlist dataview':{
itemclick: this.loadWindow
},
});
},
onLaunch: function() {
var dataview = this.getAgentData(),
store = this.getAgentsStore();
dataview.bindStore(store);
},
loadWindow: function(view, record, item, index, e){
if(!Ext.getCmp('agent-window'+record.get('id'))){
var window = Ext.create('Cc.view.agent.Window', {
title: 'À propos de '+record.get('firstname')+' '+record.get('lastname'),
id: 'agent-window'+record.get('id')
}),
tabPanel = window.getTabPanel();
absencePanel = window.getAbsencePanel();
store = absencePanel.getGrid().getStore();
absencePanel.enable();
tabPanel.add(absencePanel);
tabPanel.setActiveTab(absencePanel);
store.getProxy().url = '/person/' + record.get('id') +'/absences';
store.load();
}
Ext.getCmp('agent-window'+record.get('id')).show();
}
});
and the absence.Panel view, container of absence.Grid :
Ext.define('Cc.view.absence.Panel', {
extend: 'Ext.panel.Panel',
alias: 'widget.absencepanel',
title: 'Mes absences',
iconCls: 'item-outils',
closable: true,
border: false,
disabled: true,
layout: 'border',
initComponent: function() {
this.grid = Ext.create('Cc.view.absence.Grid', {
region: 'center'
});
this.history = Ext.create('Cc.view.absence.History', {
region: 'south',
height: '25%'
});
Ext.apply(this, {
items: [
this.grid,
this.history
]
});
this.callParent(arguments);
},
getGrid: function(){
return this.grid;
},
getHistory: function(){
return this.history;
}
});
Yes. Here is more details explanation of what I am doing. I hope you have read the forum completely. There is still one information that we are not clear about. That is the exact use of using "stores" property in controller. IMHO Sencha team should explain the MVC in much more detail with complex examples.
Yes, what you have newly posted is correct. when you have create new view, create a new instance of the store! Now, from the forum discussions, people argue about MVC. I would definitly go with steffenk . What we are doing here is injecting a new instance of store to my view. And I am ignoring the stores
property of the controller.
Here is an example:
This is my view. Its a panel (with user profile information) that I display on my tabpanel :
Ext.define('Dir.view.profile.View' ,{
extend: 'Ext.panel.Panel',
alias : 'widget.profileview',
title : 'Profile',
profileId: 1, // default and dummy value
initComponent: function() {
// configure necessary stuff, I access my store etc here..
// console.log(this.profileStore);
this.callParent(arguments);
},
// Other view methods goes here
});
Now, look at my controller:
Ext.define('Dir.controller.Profile', {
extend: 'Ext.app.Controller',
//stores: ['Profile'], --> Note that I am NOT using this!
refs: [
{ref:'cp',selector: 'centerpane'}
],
views: ['profile.View'],
init: function() {
// Do your init tasks if required
},
displayProfile: function(selectedId) {
// create a new store.. pass your config, proxy url etc..
var store = Ext.create('Dir.store.Profile',{profileId: selectedId});
console.log('Display Profile for ID ' + selectedId);
// Create instance of my view and pass the new store
var view = Ext.widget('profileview',{profileId: selectedId,profileStore: store});
// Add my new view to centeral panel and display it...
this.getCp().add(view);
this.getCp().setActiveTab(view);
}
});
My displayProfile()
is called from some event listeners (Menu, Tree etc) and it passes a id. My controller using this id to setup a new store and view. I hope the above code gives you a clear picture of what I said yesterday.
In your controller, you will have to add Ext.require('Dir.store.Profile');
so that ExtJS know you have such a store. This is because we are not making use of stores
property.
Now, you can want to reuse these created stores elsewhere, you can add them to the StoreManager. With this, you can access your created stores at any place, add and remove stores. But this comes with a overhead of you managing the instances of the store.
Why do you share the same instance of the store with different views? when you have a new view, create a new instance of the store. This will prevent updated data appearing on other windows when one is refreshed.
精彩评论