Extjs4原始碼解釋TreeStore的autoLoad無效
這個是我轉載網上的一位大牛的分析,很給力,特此轉載
這幾天遇到個問題。就是在用extjs4的TreeStore的時候,不想讓他自動載入,但是發現設定了autoload為false也沒有用,被逼急了,用firebug一步步的更進去看重要找到原因了,下面我們將通過Extjs自帶的例子
來解釋. 我是用的extjs4.0.7.http://server/ext-4.0.7-gpl/examples/tree/check-tree.html
例子的TreeStore的程式碼如下:
<!-- lang: js -->
Ext.onReady(function() {
var store = Ext.create('Ext.data.TreeStore' , {
proxy: {
type: 'ajax',
url: 'check-nodes.json'
},
sorters: [{
property: 'leaf',
direction: 'ASC'
}, {
property: 'text',
direction: 'ASC'
}],
autoLoad : false
});
var tree = Ext.create('Ext.tree.Panel', {
store: store,
rootVisible: false ,
useArrows: true,
frame: true,
title: 'Check Tree',
renderTo: 'tree-div',
width: 200,
height: 250,
dockedItems: [{
xtype: 'toolbar',
items: {
text: 'Get checked nodes',
handler: function(){
var records = tree.getView().getChecked(),
names = [];
Ext.Array .each(records, function(rec){
names.push(rec.get('text'));
});
Ext.MessageBox.show({
title: 'Selected Nodes',
msg: names.join('<br />'),
icon: Ext.MessageBox.INFO
});
}
}
}]
});
});
上面的程式碼首先是建立了個Ext.data.TreeStore. 這樣我們就可以定位到Ext.data.TreeStore,這個類中有1個方法要特別注意,是setRootNode: function(root),下面是方法的主要內容
<!-- lang: js -->
setRootNode: function(root) {
...
Ext.data.NodeInterface.decorate(root);
...
// If the user has set expanded: true on the root, we want to call the expand function
if (!root.isLoaded() && (me.autoLoad === true || root.isExpanded())) {
me.load({
node: root
});
}
return root;
}
從上面可以看出真正發出load方法是在setRootNode方法中,可以出發load的條件是!root.isLoaded() && (me.autoLoad === true || root.isExpanded()). 很顯然root.isLoaded()是false,因為在構造store的時候還沒有載入過資料;autoLoad肯定是false,這個是我們自己設定的;那麼唯一有問題的就是isExpanded方法。這個好辦我們在建立Ext.data.TreeStore加入root不久成了,好了,下面是我們改過的程式碼:
<!-- lang: js -->
var store = Ext.create('Ext.data.TreeStore', {
proxy: {
type: 'ajax',
url: 'check-nodes.json'
},
sorters: [{
property: 'leaf',
direction: 'ASC'
}, {
property: 'text',
direction: 'ASC'
}],
autoLoad : false,
root : {
expanded : false
}
});
然後滿懷期待的執行,發現還是一樣的。為什麼呢。繼續firebug。
根據上面的經驗我們發現expanded是問題的關鍵。那麼就分成三種情況.
- 設定Root,expanded設定成true
- 不設定Root。
- 設定Root,expanded不設定任何值
- 設定Root,expanded設定成false
第一種情況肯定會自動載入。那麼我們先看第二種情況
不設定Root
這種情況下程式碼將會在在建立完成TreeStore之後進入TreePanel的initComponent方法,而這個方法有個很重要的程式碼如下:
<!-- lang: js -->
initComponent: function() {
if (!me.getView().rootVisible && !me.getRootNode()) {
me.setRootNode({
expanded: true
});
}
}
上面可以看到如果沒有設定,那麼me.getRootNode()為空,就會建立個預設的值{expanded: true}. 這樣在expanded變成了true。所以自動載入
設定Root,expanded不設定任何值
這種情況其實是屬於第四種的。因為在上面setRootNode方法中可以看到。Ext.data.NodeInterface.decorate(root);這句話,這個就是為沒有分配的屬性分配預設值。跟進去可以看到預設值是false。我們就直接進入下一個情況的分析
設定Root,expanded設定成false
這中情況比較複雜。 前面都一樣,第一步建立了TreeStore,然後進入TreePanel的constructor,再進入TreePanel的initComponent,在initComponent裡面呼叫了me.callParent();。這句話就是呼叫父類的initComponent。這樣我們進入父類Ext.panel.Table看一下大概的程式碼如下。
<!-- lang: js -->
initComponent: function(){
...
// AbstractDataView will look up a Store configured as an object
// getView converts viewConfig into a View instance
view = me.getView();
....
},
/**
* Gets the view for this panel.
* @return {Ext.view.Table}
*/
getView: function() {
var me = this,
sm;
if (!me.view) {
sm = me.getSelectionModel();
me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
deferInitialRefresh: me.deferRowRender,
xtype: me.viewType,
store: me.store,
headerCt: me.headerCt,
selModel: sm,
features: me.features,
panel: me
}));
me.mon(me.view, {
uievent: me.processEvent,
scope: me
});
sm.view = me.view;
me.headerCt.view = me.view;
me.relayEvents(me.view, ['cellclick', 'celldblclick']);
}
return me.view;
}
在initComponent裡面呼叫了getView方法。在getView方法中先判斷是否已經有view存在。沒有的話新建立一個。通過me.createComponent建立,這裡要注意下me.viewConfig,因為我們在定義TreeStore的時候定義了root屬性,所以在viewConfig裡的node屬性不是null也不是undefined型別,這個很關鍵。建立了createComponent之後我們就進入了Ext.tree.View的initComponent方法。程式碼如下
<!-- lang: js -->
initComponent: function() {
var me = this;
if (me.initialConfig.animate === undefined) {
me.animate = Ext.enableFx;
}
me.store = Ext.create('Ext.data.NodeStore', {
recursive: true,
rootVisible: me.rootVisible,
listeners: {
beforeexpand: me.onBeforeExpand,
expand: me.onExpand,
beforecollapse: me.onBeforeCollapse,
collapse: me.onCollapse,
scope: me
}
});
if (me.node) {
me.setRootNode(me.node);
}
me.animQueue = {};
me.callParent(arguments);
}
終於找到罪魁禍首了就是
<!-- lang: js -->
if (me.node) {
me.setRootNode(me.node);
}
因為上面我們說了node不是null所以會呼叫setRootNode,看下setRootNode方法:
<!-- lang: js -->
setRootNode: function(node) {
var me = this;
me.store.setNode(node);
me.node = node;
if (!me.rootVisible) {
node.expand();
}
}
這裡會判斷rootVisible。預設的是false的。所以會呼叫expand()方法。這個方法會出發一系列的事件。最終會到TreeStore的onBeforeNodeExpand方法了. 看下TreeStore的onBeforeNodeExpand方法:
<!-- lang: js -->
onBeforeNodeExpand: function(node, callback, scope) {
if (node.isLoaded()) {
Ext.callback(callback, scope || node, [node.childNodes]);
}
else if (node.isLoading()) {
this.on('load', function() {
Ext.callback(callback, scope || node, [node.childNodes]);
}, this, {single: true});
}
else {
this.read({
node: node,
callback: function() {
Ext.callback(callback, scope || node, [node.childNodes]);
}
});
}
},
好了看到了,最終會到最後一個else分支上。呼叫this.read. read方法內會呼叫ajax的請求。
經過上面的程式碼分析。所以給出的解決方案有兩種
方案1
定義TreeStore的時候配置如下內容:
<!-- lang: js-->
autoLoad : false,
root: {
expanded : false
}
定義TreePanel的時候配置rootVisible: true。
方案2
重寫TreeStore的setRootNode方法
<!-- lang: js -->
Ext.override(Ext.data.TreeStore, {
setRootNode: function(root) {
var me = this;
root = root || {};
if (!root.isNode) {
Ext.applyIf(root, {
id: me.defaultRootId,
text: 'Root',
allowDrag: false
});
root = Ext.ModelManager.create(root, me.model);
}
Ext.data.NodeInterface.decorate(root);
me.getProxy().getReader().buildExtractors(true);
me.tree.setRootNode(root);
//主要修改
//if(!root.isLoaded() && (me.autoLoad === true || root.isExpanded())){
if (!root.isLoaded() && (me.autoLoad === true)) {
me.load({
node: root
});
}
return root;
}
});
這種情況不能設定TreeStore的root屬性。
當然還有其他的方法。基本都是重寫原來的邏輯。現在想想。貌似extjs是為了實現動態載入資料設定的這些屬性。