我使用的extjs版本是4.1.0 ,在Ext.data.Model中, 有一个属性叫phantom,平时不太用到,也没多注意,直到这2天我们team一个js专家遇到一个很棘手的问题,这才触发我去窥测到这个属性的作用。
老习惯,我们还是从需求开始:
需求:
用Extjs的树控件,我们需要在树控件的目录图标(比如 address-service)上双击后,
会创建一个子目录,并且这个子目录的图标前面要有一个"+" 号。
一开始,我们觉得很简单,Controller的代码如下:
this.control({ '#svctree' : { beforeitemexpand : function(obj, e) { var node =obj.createNode ({text:'asdsdsds',expandable: true}); obj.appendChild(node,null,true); } },
结果大失所望,我们点击address-service后,新目录(asdsdsds)是创建了,但是前面并没有"+"号:
这是什么原因呢?
分析:
费了好大劲,才发现是Ext.data.Model的phantom属性在作怪,这个属性干嘛用的呢?给出官网的说明:
我的理解是,这个字段为true,则表示Record是不和存储(store)同步的,也就是脏数据,如果为false,则是同步的
联系到我们的例子,后来我又去比较了extjs 4.1.0和以前用过的extjs 4.0.7,终于发现了他们的不同,在于createNode()方法,结论是,在extjs 4.1.0中,当调用createNode()来创建树节点时候,它是直接返回的新创建的node对象,这个node对象的phantom属性为true,所以在appendChild()时候,就发生了我们最上面的例子,而在extjs 4.0.7中,它并不直接返回node对象,而是返回NodeInterface decorate()后的node对象,而这个decorate()方法会调用Ext.data.Record的commit()方法,而在这个commit()方法中,它会显式的吧phantom属性设置为false ,所以就不会有我们上述问题了。
具体说来:
在extjs 4.1.0中,当执行到createNode()时候,我们跟进看:
在extjs-all-debug.js的38490行,它是直接返回node的,而这个node,从右边可以看到,它的值是true.所以出了我们开始的问题。
而在extjs 4.0.7中,同样对于createNode,它有着不同的实现,如下53402行所示,它会返回decorate(node)后的结果:
而这个NodeInterface 的decorate()方法, 会调用Record的commit()方法,见34行:
Ext.define('Ext.data.NodeInterface', { requires: ['Ext.data.Field'], statics: { decorate: function(record) { if (!record.isNode) { var mgr = Ext.ModelManager, modelName = record.modelName, modelClass = mgr.getModel(modelName), idName = modelClass.prototype.idProperty, newFields = [], i, newField, len; modelClass.override(this.getPrototypeBody()); newFields = this.applyFields(modelClass, [ … ]); len = newFields.length; for (i = 0; i < len; ++i) { newField = newFields[i]; if (record.get(newField.name) === undefined) { record.data[newField.name] = newField.defaultValue; } } } …. record.commit(true); …
而Ext.data.Record的commit()方法,会显式的吧phantom属性设置为false,见第7行:
Ext.define('Ext.data.Model', { alternateClassName: 'Ext.data.Record', … commit : function(silent) { var me = this; me.phantom = me.dirty = me.editing = false; me.modified = {}; if (silent !== true) { me.afterCommit(); } }, …
所以,在这种逻辑下,createNode()方法返回的Node的phantom属性就为false ,所以我们接下来在appendChild就不会有任何问题。
解决方法:
其实说到这里,很明了了,如果你用的是extjs 4.0.7 ,那么代码就是我最上面的那段代码。如果用的是extjs4.1.0,那么我们必须在appendChild之前吧phantom属性值显式设置为false,如下第5行:
this.control({ '#svctree' : { beforeitemexpand : function(obj, e) { var node =obj.createNode ({text:'asdsdsds',expandable: true}); node.phantom=false; obj.appendChild(node,null,true); } },
最终,因为我们用的是4.1.0,所以我们用了第二种方法,结果如预期所料: