网上这样的控件不少,有很多实现方法,其中不少的很巧妙。
例如:在文本框(HTML INPUT)后,添加一个隐藏层,隐藏层上放一个下拉列表,存储了所有可能的元素,每次文本框触发onpropertychange事件,就是对隐藏层上下拉列表数据的过滤显示。而选择下拉列表,则对文本框进行赋值......然后我们可以通过一些CSS技巧,将它的外观调整成一个可输入可联想下拉列表的模样。
当然,本文并不打算对上述实现方式进行讨论,而是将EXT中一些实用的控件,“便宜地”添加到我们的项目中来(EXT版本1.1.1)。是的,我强调的是“便宜”!
EXT上有一种控件ComboBox,就包含了这样的特性,而我们要使用它,标准的做法是:
1. 首先,你需要将ext-1.1.1(如果你要使用它的样式,那么里面的resource是必须的——样式定义,以及它所依赖的图片都放在里面)添加到项目中来,在需要使用ComboBox的页面添加JS(必须)和CSS引用路径:
<
link
rel
="stylesheet"
type
="text/css"
href
="../js/ext-1.1.1/resources/css/ext-all.css"
/>
<
script
type
="text/javascript"
src
="../js/ext-1.1.1/adapter/ext/ext-base.js"
></
script
>
<
script
type
="text/javascript"
src
="../js/ext-1.1.1/ext-all.js"
></
script
>
注意路径哦!
2. 你需要一个载体,EXT就是这样,布局需要document.body,Grid需要<div>,而ComboBox则需要一个<input type="text">:
<
input
type
="text"
id
="local1"
/>
3. 新建一个JS文件,其间包括对存储结构的定义和ComboBox属性的定义:
Ext.onReady(
function
(){
Ext.QuickTips.init();
var
data
=
[
[
'
1
'
,
'
Lislie
'
,
'
D005
'
,
'
male
'
],
[
'
2
'
,
'
Merry
'
,
'
D004
'
,
'
female
'
],
[
'
3
'
,
'
Edison
'
,
'
D003
'
,
'
male
'
],
[
'
4
'
,
'
Mark
'
,
'
D002
'
,
'
male
'
],
[
'
5
'
,
'
Leeon
'
,
'
D001
'
,
'
male
'
]
];
//
格式化数据
var
ds
=
new
Ext.data.Store({
proxy:
new
Ext.data.MemoryProxy(data),
//
数据源
reader:
new
Ext.data.ArrayReader({}, [
//
如何解析
{name:
'
id
'
},
{name:
'
name
'
},
{name:
'
depno
'
},
{name:
'
sex
'
}
])
});
ds.load();
var
storeList
=
new
Ext.form.ComboBox({
store: ds,
valueField:
'
id
'
, // option.value
typeAhead:
true
,
displayField:
'
name
'
, // option.text
triggerAction:
'
all
'
,
emptyText:
'
Select a store...
'
,
mode:
'
local
'
,
selectOnFocus:
true
,
width:
135
});
storeList.applyTo(
'
local1
'
);
});
上面的数据来源是一个写死的2维数组。同样的道理,你的数据源可以来自一个链接——请求一个页面,而该页面的Response返回一个符合Json格式的字符串(EXT里面最常用的存储方式是JSON),那么Ext.data.Store的定义就变成了这样:
var
store
=
new
Ext.data.Store({
proxy:
new
Ext.data.ScriptTagProxy({ url:
'
http://localhost:17319/KBS/Grid/EditString.aspx
'
}),
//
数据源
reader:
new
Ext.data.JsonReader({
//
解析格式
totalProperty :
'
totalCount
'
,
//
用于分页
root :
'
root
'
,
id :
'
id
'
},
[
{name:
'
id
'
},
{name:
'
name
'
},
{name:
'
depno
'
},
{name:
'
sex
'
}
])
});
如此,需要请求页面返回的String符合这样的格式,例如:
{
'totalProperty':100,
'root':[
{'id':'a1', 'name':'Lislie', 'depno':'D001', 'sex':'Male'},
{'id':'a2', 'name':'Merry', 'depno':'D002', 'sex':'Female'},
{'id':'a3', 'name':'Jone', 'depno':'D003', 'sex':'Female'},
{'id':'a4', 'name':'Mark', 'depno':'D004', 'sex':'Male'}
]
}
网上有不少将DataTable或Model转JSON的工具类,当然也也可以自己写,反射+字符串的拼接。
3. 最后,将这个JS引用到页面上。那么一个可输入可联想的控件便出来了。这里有一个小的BUG(暂且用这个词吧),ComboBox终究是放在一个<input>上。
在前台,你可以通过storeList.getValue()获取当前的选中项的option.value(如果是手动输入的项,而非经过联想选中的项,storeList.getValue()得到的是'',你只能获取到storeList.getRawValue()即<input>的value。需要在点击保存按钮时,在onclick事件中将两者的值设置为一致:“onclick='javascript:storeList.setValue(storeList.getRawValue);'”)。
而在后台,你无法像操作<select>一样,获取它的option.value,而只得到它的text。也只能操作这个Text了。
总结:如果你的数据库操作放在前台,那么你完全可以将ComboBox当成一个<select>(功能更强大,允许你输入不存在的项)。如果你的数据库操作放在后台,那么,你只能操作它的Text了,而所谓的可联想、可输入下拉列表,其实是一个糊弄人的文本框而已。
------------------------------------------------------------------------------
老实说,上面的实现过程还是很麻烦,这里有一个很简单的实现方式——其实EXT研发小组已经替我们完成了这一步,是的,仔细看API,你会发现:
你可以将一个带数据的<select>轻松的转化为ComboBox,哪怕它是asp的控件<asp:DropdownList>。而这个转化过程,就是ComboBox的一行属性:
var
storeList;
Ext.onReady(
function
(){
Ext.QuickTips.init();
storeList
=
new
Ext.form.ComboBox({
typeAhead:
true
,
triggerAction:
'
all
'
,
emptyText:
'
Select a store...
'
,
mode:
'
local
'
,
selectOnFocus:
true
,
transform:
'
AList
'
,
//
将已存在select(通过id)直接转ComboBox
width:
135
});
})
缺陷是,手动输入的不存在的项,在后台无法取到。
两种方式区别:由于载体的不同,一个<input>,一个<select>,导致在后台,我们取值方法和取的内容也不同。虽然说,两者在外观和功能上一模一样,但后者才能真正算可输入可联想的下拉列表吧!
附(效果图):