一个基于jQuery的可重用的无限制级别联动Select。

[b][color=gray]这标题起得好长好牛B,喔呵呵呵呵……[/color][/b]

忙活一下午,写出这么个玩意,其中emptySelect和loadSelect方法的代码来自《jQuery in Action》一书,稍有改动。childSelect是自己照着加的,可能写得不太好看。

(function($){$.fn.loadSelect = function(optionsDataArray){
return this.each(function(){
if(this.tagName == 'SELECT'){
var selectElement = this;
$.each(optionsDataArray, function(index, optionData){
selectElement.options.add(new Option(optionData.text, optionData.value));
});
var defaultOption = new Option('\u8bf7\u9009\u62e9', '');
defaultOption.selected = true;
selectElement.options.add(defaultOption);
}
});
};

$.fn.childSelect = function(selector, makeURL){
var elem = this[0];
if(selector === undefined){
return elem.child ? this.pushStack(elem.child) : undefined;
}

elem.child = $(selector)[0];
this.change(function(event){
var childSelect = $(this).childSelect();
childSelect.emptySelect();
if($(this).val()){
childSelect.attr('disabled', false);
$.getJSON(makeURL.apply($(this)), function(data){
childSelect.loadSelect(data).change();
});
}else{
childSelect.attr('disabled', true);
}
if(childSelect.childSelect()) childSelect.change();
});
return this.pushStack(elem.child);
};

$.fn.emptySelect = function(){
return this.each(function(){
if(this.tagName == 'SELECT')
this.options.length = 0;
});
};})(jQuery);


这里解释一下childSelect的用法:
这个方法是用来给一个select元素设置子级select元素的,一旦设置了子级select元素,父select元素的change事件触发时将通过$.getJSON向服务器发送一个请求,并将返回的json填充到子级select元素中。如果想让父select元素的某一项选中时不发送请求,只要将这一项的value设置成空字符串''即可:如
<select><option value=''>请选择</option></select>

参数selector用来选择子select元素,跟$(selector)的用法一样。
makeURL必须是一个方法,这个方法用于创建要请求的url,上下文(即this)为父select元素的包装集对象(即jQuery包装后的父select元素)。
这个方法类似于val()方法——省略参数的时候是用于获取子select元素的包装集。
这个版本的代码对服务端返回的JSON的格式有一些要求,返回的JSON数组必须是这种格式:
[{text:'一年1班', value:'1'},{text:'一年2班', value:'2'}]


使用方法如下:
$('#grade').childSelect('#class', function(){return 'grades/'+this.val()+'/classes'}).end().change()

也可以链式地继续往后面添加子select元素的子select元素,或者直接给子select元素添加事件。

接着我遇到了一些问题。rails的to_json生成的json格式一般是这样的
[{class_name1:{attr1.1:value1.1,attr1.2:value1.2}},{class_name2:{attr2.1:value2.1,attr2.2:value2.2}}]
,没什么规律的,或者说规律比较复杂,要给select元素填充不同的ruby类数组的话,就必须知道这个ruby类的类名、用来做text和value的属性名。
我修改了一下loadSelect和childSelect方法,如下:
(function($){$.fn.loadSelect = function(optionsDataArray, hash){
return this.each(function(){
if(this.tagName == 'SELECT'){
var selectElement = this;
$.each(optionsDataArray, function(index, optionData){
selectElement.options.add(new Option(optionData[hash.className][hash.text], optionData[hash.className][hash.value]));
var defaultOption = new Option('\u8bf7\u9009\u62e9', '');
defaultOption.selected = true;
selectElement.options.add(defaultOption);
});
}
});
};

$.fn.childSelect = function(selector, makeURL, hash){
var elem = this[0];
if(selector === undefined){
return elem.child ? this.pushStack(elem.child) : undefined;
}

elem.child = $(selector)[0];
this.change(function(event){
var childSelect = $(this).childSelect();
childSelect.emptySelect();
if($(this).val()){
childSelect.attr('disabled', false);
$.getJSON(makeURL.apply($(this)), function(data){
childSelect.loadSelect(data, hash).change();
});
}else{
childSelect.attr('disabled', true);
}
if(childSelect.childSelect()) childSelect.change();
});
return this.pushStack(elem.child);
};})(jQuery);

就是给方法添加了一个hash(或者说对象)作为参数,让客户端代码来指定ruby类的类名、用于text和value的属性名,使用的时候就是多传一个hash,像这样:
$('#grade').childSelect('#clazz', function(){return 'grades/'+this.val()+'/classes'}, {className:'clazz', value:'id', text:'class_no'}).end().change();


还有另外一种解决方法,就是给rails的每个对象添加一个json_for_select方法,生成这种格式的JSON:
[{text:'一年1班', value:'1'},{text:'一年2班', value:'2'}]
,这样就可以免去上面多出来的那个hash。

问题是……json_for_select该怎么写,我还没有头绪……似乎要同时去修改ActiveSuppor::JSON和Object。

===========================================================
改好了,在config/initializers下添加一个extension.rb,内容:
class Object
def json_for_select(hash)
json = to_json(:only => [hash[:text], hash[:value]])
json = json.gsub(Regexp.new("\"#{hash[:text]}\":"), "\"text\":").gsub(Regexp.new("\"#{hash[:value]}\":"), "\"value\":")
json = json.gsub(/\{"\w+":\s\{/, '{').gsub(/\}\}/, '}') if ActiveRecord::Base.include_root_in_json
json
end
end

解释:rails启动的时候会自动加载config/initializers下的文件并执行。ActiveRecord::Base.include_root_in_json如果为true,to_json生成的JSON带有类名,如果为false,则不带类名。最后把controller中的
format.html { render :json => @classes.to_json }
换成
format.html { render :json => @classes.json_for_select(:text =>'class_no', :value=>'id')
即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值