Rails中应用Ext.tree:以中国的省市地区三级联动选择为例

在  Ajax级联选择框:以中国的省市地区三级联动选择为例  篇中介绍了Rails应用Ajax技术实现三级联动菜单,本篇将介绍Rails应用ExtJs技术实现三级联动菜单。

 

需求: 允许用户只选择省、市、地区三者之一,同时 为了简化数据库表的结构,只设计一个字段(如, cid字段)来保留省、市、地区三者之一的id,这与  Ajax级联选择框  有所不同,后者是用三个字段来分别保存省、市、地区的id。

同时也是测试Rails应用ExtJs技术的效果。

 

步骤:

①、工具准备:1、 安装Rails插件acts_as_nested_set(附件)

2、下载ext的js文件包(附件), 将其解压到public目录下

 

②、确定数据库的设计,下面将省市地区数据以如下方式存入数据库的cities表(附件):
+--------+--------------+-----------+------+------+
| id     | name         | parent_id | lft  | rgt  |
+--------+--------------+-----------+------+------+
|   4122 | 中国         |         1 |    1 | 6464 |
| 110000 | 北京市       |         0 |    2 |   39 |
| 110101 | 东城区       |    110000 |    3 |    4 |
| 110102 | 西城区       |    110000 |    5 |    6 |
| 110103 | 崇文区       |    110000 |    7 |    8 |
| 110104 | 宣武区       |    110000 |    9 |   10 |
+--------+--------------+-----------+------+------+

这个数据库表是在Ajax级联选择框  篇的附件中的cities表基础上,结合ext tree的需要设计的,其中parent_id为父节点id、lft为左节点id、rgt为右节点id。至于如何生成parent_id、lft和rgt的值 ,具体请步骤为:遍历citis  -> 用sql 查出 下级 ->调用acts_as_nested_set插件的 add_child(child_object)方法,那么就会自动生成 parent_id、lft和rgt的值。

 

例如:参考: http://www.iteye.com/topic/177501

root = Category.create( :text  =>  'Root' )   

root.add_child(c1 = Category.create( :text  =>  'Child 1' ))   

c1.add_child(Category.create( :text  =>  'Child 1.1' ))

 

 ③、在cities表对应的模型内添加代码,声明、使用acts_as_nested_set插件,如下面代码片段所示:

class City < ActiveRecord::Base   

  acts_as_nested_set   

  def children_count   

     return (self[right_col_name] - self[left_col_name] - 1)/2  

  end    

  def leaf?

      unknown? || children_count == 0  

  end              

  ............

end

 

 ④、然后修改相应的Controller:

class CitiesController < ApplicationController

  def cities_tree (id = params[:node])  

    cdata = Array.new  

    c =  City.find_all_by_parent_id( id || 1,:order => 'lft')

    cdata = get_tree(c, nil)

    render :text=>cdata.to_json , :layout=>false   

  end 

  

  def get_tree(c, parent)  

    data = Array.new  

    c.each { |cc|  

      if !cc.leaf?               

        if data.empty?  

          data =   [{"text"  =>  cc.name,"id"  => cc.id, "cls" => "folder" ,"leaf"  => false }]    

        else  

          data.concat([{"text"  =>  cc.name,"id"  => cc.id, "cls" => "folder" ,"leaf"  => false}])  

        end   

      else  

        data.concat([{"text" => cc.name,"id" => cc.id, "cls" => "file","leaf" => true}])   

      end              

    }  

    return data         

  end    

  

  def index     

    @cities = City.all

    respond_to do |format|

      format.html 

      format.xml  { render :xml => @cities }      

    end

  end    

end

 

 

⑤、 最后修改相应的视图:如公司所在地的选择框,首先所在地被保存在companies表的cid字段,那么在views/companies/new.html.erb,就可应用Ext.tree控件,实现公司所在地的树型选择,如下代码片段所示:

首先引入Ext的js文件:<%= javascript_include_tag :defaults %> 

<%= stylesheet_link_tag "../ext/resources/css/ext-all.css" %>  

<%= javascript_include_tag "../ext/adapter/prototype/ext-prototype-adapter.js" %>  

 <%= javascript_include_tag "../ext/ext-all.js" %>  

 <%= javascript_include_tag "../ext/ComboBoxTree.js" %>  

 

其次使用ComboBoxTree生成下拉选择框:

<div id="comboboxtree" ></div> 

 <% javascript_tag do  %> 

    Ext.onReady(function(){     

      var comboBoxTree = new Ext.ux.ComboBoxTree({    

        renderTo : 'comboboxtree',    

          name : 'company[cid]',   //companies表的 cid 字段

                id : 'company_cid',      //companies表的 cid 字段

        width : 250,

        border: true, 

        valueField:"id", 

        tree : new Ext.tree.TreePanel({

           loader: new Ext.tree.TreeLoader({  

               url:'/cities/cities_tree',    

             requestMethod:'GET',  

             baseParams:{format:'json' }  

           }),   

           animate:true,

           enableDD:true, 

           border:false,

           containerScroll: false,

           rootVisible:false  ,

           root: new Ext.tree.AsyncTreeNode({text: '中国',id:'0'})

         })

      }); 

      comboBoxTree.render('comboboxtree');

   });   

<% end  %>      

 

上面的代码,是参照ExtJs官方网站上使用Ext.Tree控件的例子,修改的只是,数据提供部分,即上面的粗体部分。

下图是上代码的显示效果:用户选择了: 辽宁省 --> 鞍山市 --> 岫岩满族自治县

 

 

最终保存到companies 表的 cid 字段的值就是 "岫岩满族自治县" 对应于cities表里的id值,当然用户也可以只选择 " 辽宁省 --> 鞍山市" 或  "辽宁省" ,那么相应存入数据库的就是" 鞍山市" 的id 或 " 辽宁省" 的id。

而显示的时候,只要在cities表对应的Model添加一个encode()方法,利用acts_as_nested_set插件的self_and_ancestors来找到它的上级信息,如下代码片段所示:

 

def self.encode(id)
    str = ''
    if not id.nil? then
      begin
       self.self_and_ancestors.each {|c|
        if not (c.name.include? '中国')
          str += c.name
         end
        }
        return str
    rescue Exception => exc
      logger.error("#{exc.message}")
      return ""
    end
   end
 end 

 

视图里调用<%=h City.encode(@company.cid ) %>,那么虽然@company.cid = "岫岩满族自治县" 的id,但是显示的却是完整信息:"辽宁省鞍山市岫岩满族自治县"。 可见,在Rails项目中使用ExtJs技术是没问题的,而且 效果符合RIA(富客户端)的要求。

 

参考资料:http://www.iteye.com/topic/177501

http://www.iteye.com/topic/76860

http://api.rubyonrails.org/classes/ActiveRecord/Acts/NestedSet/ClassMethods.html

 

 

 

 

你可能感兴趣的:(JavaScript,Ajax,json,ext,Rails)