After some researches, my friend posted a review for some Js frameworks (JavaScript UI framework comparison or Why I choose ExtJS & JQuery) we started to write out project mainly on ExtJs. So the first thing that needed very much and couldn’t find anywhere was the simplest combobox which default value I would gave from XML. At the process I found that Ext.data.XmlStore doesn’t have any parameter which shows is it loaded already or not. So the first I’ve extended it for my needs. Here it is
Ext.namespace('evag.data');
evag.data.XmlStore = Ext.extend(Ext.data.XmlStore, {
isLoaded: false,
constructor: function(config){
evag.data.XmlStore.superclass.constructor.call(this, config);
this.addListener('load',
function(){
this.isLoaded = true;
}
);
}
});
nothing special just adding a listener on load event and changing isLoaded property to true.
Lets consider that combo box receives an XML like this.
<?xml version="1.0" encoding="UTF-8"?>
<response>
<dataset>
<itemCount>7</itemCount>
<item id="1">
<id>1</id>
<description>English</description>
<selected/>
</item>
<item id="2">
<id>2</id>
<description>French</description>
<selected>1</selected>
</item>
<item id="3">
<id>3</id>
<description>Italian</description>
<selected/>
</item>
<item id="4">
<id>4</id>
<description>Spanish</description>
<selected/>
</item>
<item id="5">
<id>5</id>
<description>German</description>
<selected/>
</item>
<item id="6">
<id>6</id>
<description>Portuguese</description>
<selected/>
</item>
<item id="7">
<id>7</id>
<description>Chinese</description>
<selected/>
</item>
</dataset>
</response>
As you see selected nodes are empty everywhere except item with id equal 2. It means that combo box will show French selected when it finished loading XML. To tell combo box how to find the right node in XML just pass config named selected which is the xpath to that node. It equals /dataset/item:has(selected:nodeValue(true))/id in our case.
The main idea of how this feature will be implemented is that all thing must be done manually, i.e. combo box will be in local mode. Thats why I’ve added new event cload which will fire when all items added and one of them as defined in the XML will be selected. There are some required configs also to get the combo that we need. You can see them in the source given below.
There are some other features too in the source about which I will tell in future posts.
Fully working component is in the examples page.
Ext.namespace('evag.form');
evag.form.ComboBox = Ext.extend(Ext.form.ComboBox, {
// Making ComboBox local all actions of remote
// mode we will do manually
mode: 'local',
allowBlank: false,
editable: false,
triggerAction: 'all',
// Adding new property: path to selected value
selected: '',
valueNotFoundText: 'Nothing selected',
initComponent: function(){
evag.form.ComboBox.superclass.initComponent.call(this);
// Adding new events
this.addEvents(
// I've found that combobox fires change event only
// when it looses focus, so I wrote a new event
// which fires after selecting new item in combo
// see onSelect function
'realchange',
// And a new complete load event which fires when
// all combobox items are rendered and
// predefined item is selected
'cload'
);
},
/**
* Main function of the logic. It founds the selected element
* from the XmlStore and setting combobox value to it
*/
setDefaultValue: function(xmlStore){
if(Ext.isEmpty(this.selected)){
throw new Object({message: 'Config `selected` is empty.'})
}
var selectedLangId = Ext.DomQuery.selectNumber(
this.selected,
xmlStore.reader.xmlData.documentElement
);
this.setValue(selectedLangId);
this.startValue = selectedLangId;
this.unsetLoadingIndicator();
this.initValue();
this.enable();
this.fireEvent('cload', this, xmlStore);
},
setLoadingIndicator: function(){
Ext.DomHelper.insertBefore(this.el, {
tag: 'div',
html: 'Loading...',
cls: 'x-form-field loading-indicator',
style: 'height: 20px; position: absolute; margin: 2px;'
});
},
unsetLoadingIndicator: function(){
this.el.prev('.loading-indicator').remove();
},
/**
* Checking if the store is loaded. If it is loaded already, calling
* setDefaultValue function, else adding listener on load event
* to evaluate setDefaultValue function.
*/
onRender: function(component, position){
evag.form.ComboBox.superclass.onRender.call(
this, component, position
);
if(!this.store.isLoaded){
// If combobox is rendered already, but
// datastore is not loaded yet adding
// load listener to store for autoselecting
// default value of combobox after loading complete
this.disable();
this.setLoadingIndicator();
this.store.addListener('load', this.setDefaultValue, this);
}
else{
this.setDefaultValue(this.store);
}
},
/**
* Realchange event implementation
*/
onSelect: function(record, index){
this.startValue = this.getValue();
evag.form.ComboBox.superclass.onSelect.call(
this, record, index
);
if(Number(this.getValue()) !== Number(this.startValue)){
this.fireEvent(
'realchange', this,
this.getValue(), this.startValue
);
}
},
});