XML数据源的级联ComboBox

Posted on Jul 12, 2013

Background

要做一个表单,其中自然少不了ComboBox,数据源不是从后台动态获取的,竟然是一个Excel表格文件!这个数据量很大,而且还有可能会修改,所以只能采用外部加载的方式。写一个解析Excel数据的库自然是不现实的,所以决定把Excel转换成XML格式,然后再加载。

Excel转换成XML

一般直接将Excel是不能导出成XML的,因为“不包含任何XML映射”,所以首先需要建立一个XML映射,其实就是设计一个XML结构,然后将Excel数据映射到对应的节点上。这一步其实很简单,但是颇具技巧性。

“开发工具”选项卡

导出XML需要“开发工具”功能,一般Excel不会显示这个选项卡,需要打开“文件”-“选项”-“自定义功能区”对话框,勾选“开发工具”。

开发工具

设计XML结构

XML结构根据自己要求设计,多个相同子节点表示可以重复。比如这个:

<?xml version="1.0" encoding="UTF-8"?>
<root>
	<province name="">
	 	<city name="">
	 		<dealer name="" address=""/>
	 	</city>
	</province>
	<province name="">
	 	<city name="">
	 		<dealer name="" address=""/>
	 	</city>
	</province>
</root>

导入XML文件

回到Excel里,新建一个空白Excel文档,点击“开发工具”里的“源”按钮,在右侧弹出的面板里点击“XML映射”,然后添加之前的XML文件。如果成功右侧面板里会显示XML的树形结构。

导入XML文件

映射数据

这步更简单,把那个树状结构根节点拖到工作表上,工作表上会显示行以XML节点属性为名称的表头。把数据对应地粘贴到这个表里。

映射数据

导出文件

点击“源”按钮右边的“导出”,就可以导出XML了。导出后检查下数据映射对不对,如果没问题就算完成了。

加载XML数据

加载直接用URLLoader就可以。

	var url:String = xmlUrl;
	var vars:URLVariables = new URLVariables();
	var request:URLRequest = new URLRequest(url);
	 
	var loader:URLLoader = new URLLoader();
	loader.addEventListener(Event.COMPLETE, dealerInfoLoadHandler);
	loader.addEventListener(IOErrorEvent.IO_ERROR, loadErrorHandler);
	loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadErrorHandler);
	 
	loader.load(request);

级联ComboBox

由于ActionScript E4X 的处理能力很强,所以利用XML作为数据源是完全可行的。经过几个简单的处理步骤就可以作为数据源提供给ComboBox。代码如下:

	/**
	 *  加载XML数据 
	 * @param event
	 * 
	 */    
	public function dealerInfoLoadHandler(event:Event):void {
	  var loader:URLLoader = event.target as URLLoader;
	  var xml:XML = new XML(loader.data);
	  if(xml) 
	  {
		dealerXML = xml; 
		var arrProv:Array = [];
		var seen:Object={};
		var prov:XMLList = xml.province.@name.(!seen[valueOf()]&amp;&amp;(seen[valueOf()]=true));
		prov.  (
		  arrProv.push(toString())
		); 
	 
		this.provinces = arrProv;
	  }
	  cbxProv.dataProvider = new DataProvider(this.provinces);
	}
	 
	/**
	 *  省份ComboBox变化处理函数 
	 * @param event
	 * 
	 */    
	private function cbxProvChangeHandler(event:Event):void {
	  cbxCity.dataProvider.removeAll();          //清空City DP
	  var seen:Object={};    
	  var provStr:String = cbxProv.selectedItem.data;  
	  var prov:XMLList = this.dealerXML.province.(@name==provStr);  //根据省份名称从XML检索所有符合条件的城市子节点
	  var city:XMLList = prov.city.@name.(!seen[valueOf()]&amp;&amp;(seen[valueOf()]=true));  //过滤重复的城市节点
	  var arrCity:Array = [];
	  city. (
		arrCity.push(toString())  //把City数据灌到Array里
	  ); 
	 
	  cbxCity.dataProvider = new DataProvider(arrCity);
	  cbxCity.selectedIndex = 0;
	 
	  this.cbxCityChangeHandler(event);  //联动一次cbxCity
	}
	 
	/**
	 *  城市ComboBox变化处理函数 
	 * @param event
	 * 
	 */    
	private function cbxCityChangeHandler(event:Event):void {
	  cbxDealer.dataProvider.removeAll();
	  var cityStr:String = cbxCity.selectedItem.data;
	  var city:XMLList = this.dealerXML..city.(@name==cityStr);
	  var dealer:XMLList = city.dealer.@name; //末节点经销商数据不会重复,不需要过滤重复的
	  var arrDealer:Array = [];
	  dealer. (
		arrDealer.push(toString())
	  ); 
	  cbxDealer.dataProvider = new DataProvider(arrDealer);
	  cbxDealer.selectedIndex = 0;
	}

示例

总结

单纯验证了XML作为级联ComboBox数据源的可行性。如果考虑重用性和稳定性,还有一些工作要做的。不过作为临时想出来的解决方案,也完全满足要求了,还挺有趣味性的。 ;-)


参考资料

  1. http://zzfei.com/excel-to-xml