zoukankan      html  css  js  c++  java
  • ExtJs 之 ComboBox级联使用

    刚接触ExtJs不到一周,项目使用ExtJs框架,有个版块用到了combobox的级联(两级),遇到了一系列的问题,两天来一直查API、网络资料,终于解决了。

    先列出遇到的一系列问题(也许你也遇到过!),再看是如何一步步解决这些问题的,最后给出个人觉得ExtJs的ComboBox级联的最佳方案。

    ***首先声明,测试使用[年级]和[班级]的级联,数据从服务端获取。最终效果是:年级列表显示所有年级,默认显示第一个年级;班级列表显示第一个年级下的班级,默认显示"所有";***

    遇到的问题:

    1.为何每次点击班级列表时就把所有的班级加载出来了,但切换另一个年级后就正常了?

    2.打开火狐的Firebug可以看到,班级列表已经加载一次了,但点击下拉列表框后又加载了一次,怎么回事?其实点击年级列表也会再加载一次的,why?

    3.如何为combobox设置一个默认值?

    4.如何为combobox添加一个值(“所有”)

    5.想在监听事件afterrender或者change事件中来处理上述问题,觉得不是你想的那样?

    6.queryMode、triggerAction、autoLoad这些属性怎么配合使用?

     

     ----------解决-------------------------------------------------------------------------------------------------------------------------------------------------------------

    先贴出测试的Servlet类:主要用于获取年级列表和班级列表,数据是静态的,以JSON格式返回。

      1 package com.lizhou.bms.controller;
      2 
      3 import com.lizhou.bms.entity.Clazz;
      4 import com.lizhou.bms.entity.Grade;
      5 import net.sf.json.JSONArray;
      6 
      7 import javax.servlet.ServletException;
      8 import javax.servlet.http.HttpServlet;
      9 import javax.servlet.http.HttpServletRequest;
     10 import javax.servlet.http.HttpServletResponse;
     11 import java.io.IOException;
     12 import java.util.ArrayList;
     13 import java.util.LinkedList;
     14 import java.util.List;
     15 
     16 /**
     17  * 模拟获取数据
     18  * @author bojiangzhou
     19  * @date 2016/8/8
     20  */
     21 public class StudentController extends HttpServlet {
     22 
     23     private static List<Grade> gradeList = new LinkedList<Grade>();
     24 
     25     private static List<Clazz> clazzList = new LinkedList<Clazz>();
     26 
     27     /**
     28      * 数据源
     29      */
     30     static {
     31         //年级
     32         Grade g1 = new Grade(1, "一年级");
     33         Grade g2 = new Grade(2, "二年级");
     34         Grade g3 = new Grade(3, "三年级");
     35 
     36         gradeList.add(g1);
     37         gradeList.add(g2);
     38         gradeList.add(g3);
     39 
     40         //班级
     41         Clazz g1c1 = new Clazz(1, 1, "一年级 1班");
     42         Clazz g1c2 = new Clazz(2, 1, "一年级 2班");
     43         Clazz g1c3 = new Clazz(3, 1, "一年级 3班");
     44         Clazz g1c4 = new Clazz(4, 1, "一年级 4班");
     45         Clazz g1c5 = new Clazz(5, 1, "一年级 5班");
     46         Clazz g1c6 = new Clazz(6, 1, "一年级 6班");
     47         Clazz g1c7 = new Clazz(7, 1, "一年级 7班");
     48 
     49         Clazz g2c1 = new Clazz(8, 2, "二年级 1班");
     50         Clazz g2c2 = new Clazz(9, 2, "二年级 2班");
     51         Clazz g2c3 = new Clazz(10, 2, "二年级 3班");
     52         Clazz g2c4 = new Clazz(11, 2, "二年级 4班");
     53         Clazz g2c5 = new Clazz(12, 2, "二年级 5班");
     54         Clazz g2c6 = new Clazz(13, 2, "二年级 6班");
     55         Clazz g2c7 = new Clazz(14, 2, "二年级 7班");
     56 
     57         Clazz g3c1 = new Clazz(15, 3, "三年级 1班");
     58         Clazz g3c2 = new Clazz(16, 3, "三年级 2班");
     59         Clazz g3c3 = new Clazz(17, 3, "三年级 3班");
     60         Clazz g3c4 = new Clazz(18, 3, "三年级 4班");
     61         Clazz g3c5 = new Clazz(19, 3, "三年级 5班");
     62         Clazz g3c6 = new Clazz(20, 3, "三年级 6班");
     63         Clazz g3c7 = new Clazz(21, 3, "三年级 7班");
     64 
     65         clazzList.add(g1c1);
     66         clazzList.add(g1c2);
     67         clazzList.add(g1c3);
     68         clazzList.add(g1c4);
     69         clazzList.add(g1c5);
     70         clazzList.add(g1c6);
     71         clazzList.add(g1c7);
     72 
     73         clazzList.add(g2c1);
     74         clazzList.add(g2c2);
     75         clazzList.add(g2c3);
     76         clazzList.add(g2c4);
     77         clazzList.add(g2c5);
     78         clazzList.add(g2c6);
     79         clazzList.add(g2c7);
     80 
     81         clazzList.add(g3c1);
     82         clazzList.add(g3c2);
     83         clazzList.add(g3c3);
     84         clazzList.add(g3c4);
     85         clazzList.add(g3c5);
     86         clazzList.add(g3c6);
     87         clazzList.add(g3c7);
     88 
     89     }
     90 
     91     @Override
     92     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     93 
     94         //前台传一个method参数,getGradeList即请求获取年级列表,getClazzList即请求获取班级列表
     95         String method = request.getParameter("method");
     96 
     97         response.setCharacterEncoding("UTF-8");
     98 
     99         if("getGradeList".equals(method)){
    100             JSONArray jsonArray = JSONArray.fromObject(gradeList);
    101             String ret = jsonArray.toString();
    102             response.getWriter().write(ret);
    103 
    104         } else if("getClazzList".equals(method)){
    105             List<Clazz> clist = new ArrayList<Clazz>();
    106             //年级id
    107             String sgid = request.getParameter("gid");
    108             if(sgid != null){
    109                 int gid = Integer.parseInt(sgid);
    110 
    111                 for(Clazz c : clazzList){
    112                     if(c.getGid() == gid){
    113                         clist.add(c);
    114                     }
    115                 }
    116             } else{
    117                 clist.addAll(clazzList);
    118             }
    119             JSONArray jsonArray = JSONArray.fromObject(clist);
    120             String ret = jsonArray.toString();
    121             response.getWriter().write(ret);
    122 
    123         }
    124     }
    125 }

     

    然后是最初版的JS代码:

      1 <%--
      2   
      3   @author bojiangzhou
      4   @date 2016/8/8
      5 --%>
      6 <%@ page contentType="text/html;charset=UTF-8"  language="java" %>
      7 <%@ page isELIgnored="false" %>
      8 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      9 <html>
     10 <head>
     11     <title>Combobox</title>
     12     <link rel="stylesheet" href="js/ext/resources/css/ext-all.css" />
     13     <script type="text/javascript" src="js/ext/ext-all.js"></script>
     14     <script>
     15         Ext.onReady(function () {
     16 
     17             /**
     18              * 创建年级Combo
     19              */
     20             Ext.create('Ext.form.ComboBox', {
     21                 renderTo: Ext.getBody(),
     22                 id: 'gradeId',
     23                 displayField: 'name',
     24                 valueField: 'id',
     25                 editable: false,
     26                 readonly: true,
     27                 allowBlank: true,
     28                 fieldLabel: '选择年级',
     29                 margin: '50 10 0 0',
     30                 labelAlign: 'right',
     31                 triggerAction: 'all',   //点击下拉列表时执行的操作
     32                 queryMode: 'remote',    //store的查询模式
     33                 store: Ext.create('Ext.data.JsonStore', {
     34                     fields: [
     35                         {name: 'id'},
     36                         {name: 'name'}
     37                     ],
     38                     autoLoad: true, //启动自动加载
     39                     proxy: {        //通过ajax代理加载数据
     40                         type: 'ajax',
     41                         url: 'student?method=getGradeList',
     42                         reader: {
     43                             type: 'json',
     44                             root: 'content'
     45                         }
     46                     }
     47                 }),
     48                 listeners: {
     49                     'change': function(o, gid){ //change事件
     50                         if(gid){
     51                             var clazzId = Ext.getCmp("clazzId");    //获取Clazz Combo组件
     52                             clazzId.getStore().removeAll(); // 清空已加载列表
     53                             clazzId.reset();    // 清空已存在结果
     54 
     55                             //发生change事件后将年级id传到后台获取该年级下的班级
     56                             clazzId.getStore().load({
     57                                 params: {'gid': gid}
     58                             });
     59                         }
     60                     }
     61                 }
     62 
     63             });
     64 
     65             /**
     66              * 创建班级Combo
     67              */
     68             Ext.create('Ext.form.ComboBox', {
     69                 renderTo: Ext.getBody(),
     70                 id: 'clazzId',
     71                 displayField: 'name',
     72                 valueField: 'id',
     73                 editable: false,
     74                 readonly: true,
     75                 allowBlank: true,
     76                 fieldLabel: '选择班级',
     77                 margin: '50 10 0 0',
     78                 labelAlign: 'right',
     79                 triggerAction: 'all',   //点击下拉列表时执行的操作
     80                 queryMode: 'remote',    //store的查询模式
     81                 store: Ext.create('Ext.data.JsonStore', {
     82                     fields: [
     83                         {name: 'id'},
     84                         {name: 'gid'},
     85                         {name: 'name'}
     86                     ],
     87                     autoLoad: true, //启动自动加载
     88                     proxy: {        //通过ajax代理加载数据
     89                         type: 'ajax',
     90                         url: 'student?method=getClazzList',
     91                         reader: {
     92                             type: 'json',
     93                             root: 'content'
     94                         }
     95                     }
     96                 })
     97             });
     98 
     99 
    100         });
    101 
    102     </script>
    103 </head>
    104 <body>
    105 
    106 </body>
    107 </html>

     

    一、queryMode、autoLoad

    第一次刷新页面显示效果如下:可以看到两个列表都没有默认值,其次是一开始就发送了两次请求,也就是说已经将年级和班级的数据加载进来了(而且还是所有数据)。

    然后点击班级列表,选择一年级,看到如下效果:点击年级下拉列表的时候又发送了一次请求的,然后这个时候会触发年级combobox的change事件,加载班级列表,可以看到请求已经发送过去了,年级id也传过去了,那班级列表按理说应该是一年级的班级;

    再看第二张图片的效果:点击下拉列表框的时候也同样发送了一次请求,而班级显示的是所有班级,这就是出现的问题了,为什么会这样呢?

    从第一次刷新页面来说整个过程:首先刷新页面,因为配置的store为自动加载(autoLoad: true),所以在刷新页面的时候,会自动将数据加载到store中,然后渲染到列表里。

    然后点击年级列表,因为我们设置的queryMode: 'remote',(remote是默认属性值);个人理解:queryMode属性决定着当【第一次】点击下拉列表的时候,列表的查询模式,remote即从远程加载,相当于点击下拉列表的时候又加载了一次,这就是点击列表的时候为什么又发送了一次请求的原因。queryModel的另一个属性值是'local',从本地加载;我的理解是,数据如果已经从远端加载到store中了(比如autoLoad,年级列表change事件触发加载班级列表),所谓的local就是当第一次点击下拉列表的时候直接从store中获取数据,而相对的,remote则会从远端加载,而且会覆盖掉store中的数据。

    再是点击班级列表,虽然点击年级列表触发了change事件来使班级列表加载当前年级下的班级,原因上面已经说了,点击班级列表的时候,同样重新发送了一个请求加载了所有的班级,所以之前的被覆盖了。

    解决办法:将二者的queryMode设置为local,使其从Store中获取数据,年级列表自动加载,设置为local后点击下拉列表时不会再发送一次请求;但是班级列表是与年级列表联动的,所以在没有年级列表的时候,我不希望显示班级列表,那么可以设置班级ComboBox的store的autoLoad:false,让其不自动加载,只有在选择年级的时候才去加载相应年级下的班级。这样一来刷新页面的时候就只发送了一次加载年级的请求,班级只会在选择年级后加载,但是每次还是会发送请求的。

     

     二、如何为让年级列表默认选择第一个,班级列表默认显示"所有"

    让第一级列表(年级列表)默认显示第一条,刚开始想的办法是给班级Combobox加一个afterrender事件,即组件渲染完成后给年级列表设置第一个选项,这样也会触发change事件,就能加载班级了;

    或者给年级列表添加一个属性value=1,默认选择第一个选项,但是第一次不会加载班级,没有触发change事件。这两种方式都有一个小问题,就是刷新页面的时候,会看到列表框首先显示的1,再才显示第一个选项的,尤其在加载比较慢的时候就很明显了。所以这两种方式不可取。

    1 listeners: {
    2     'afterrender': function (o) {
    3         var gradeId = Ext.getCmp("gradeId");    //获取Grade Combo组件
    4         gradeId.setValue(1);
    5     }
    6 }

    再说说如何为班级列表插入一个选项"所有",之前尝试过很多种方式都不行,然后想了一个不算好的办法可以在后台获取到数据后,再向集合中插入一个含有"所有"的对象,就能直接加载过来了,但是这种方式不是很好。其实主要是添加的时机不对,导致没有添加进去。

    最后经过一系列的测试,对于数据的操作应放在Store的load事件中来操作,就都正常了,Store本身就是数据仓库,所以在ComboBox上做的操作都有所不妥。

    看最后解决上述问题的代码:注意看注释部分,是解决问题的关键。

      1 <%--
      2   
      3   @author bojiangzhou
      4   @date 2016/8/7
      5 --%>
      6 <%@ page contentType="text/html;charset=UTF-8"  language="java" %>
      7 <%@ page isELIgnored="false" %>
      8 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      9 <html>
     10 <head>
     11     <title>Combobox</title>
     12     <link rel="stylesheet" href="js/ext/resources/css/ext-all.css" />
     13     <script type="text/javascript" src="js/ext/ext-all.js"></script>
     14     <script>
     15         Ext.onReady(function () {
     16 
     17             /**
     18              * 年级列表
     19              */
     20             Ext.create('Ext.form.ComboBox', {
     21                 renderTo: Ext.getBody(),
     22                 id: 'gradeId',
     23                 displayField: 'name',
     24                 valueField: 'id',
     25                 editable: false,
     26                 readonly: true,
     27                 allowBlank: true,
     28                 fieldLabel: '选择年级级',
     29                 margin: '50 10 0 0',
     30                 labelAlign: 'right',
     31                 queryMode: 'local',     //本地查询,配置这个属性,在第一次点击下拉列表的时候就不会从服务端加载数据了
     32                 triggerAction: 'all',
     33                 store: Ext.create('Ext.data.JsonStore', {   //Store数据仓库
     34                     fields: [
     35                         {name: 'id'},
     36                         {name: 'name'}
     37                     ],
     38                     autoLoad: true,     //第一级列表设置自动加载
     39                     proxy: {            //通过ajax代理加载数据
     40                         type: 'ajax',
     41                         url: 'student?method=getGradeList',
     42                         reader: {
     43                             type: 'json',
     44                             root: 'ret'
     45                         }
     46                     },
     47                     listeners: {    //注意是store的监听器
     48                         'load': function (store, records) { //store的load事件
     49                             //设置第一个值为默认值
     50                             Ext.getCmp("gradeId").setValue(records[0]);
     51                         }
     52                     }
     53                 }),
     54                 listeners: {        //这是ComboBox的监听器
     55                     'change': function(o, nv){  //change事件
     56                         if(nv){
     57                             var clazzId = Ext.getCmp("clazzId");
     58                             clazzId.getStore().removeAll();// 清空已加载列表
     59                             clazzId.reset();// 清空已存在结果
     60 
     61                             //在年级列表发生改变时将年级ID传到后台,加载该年级下的班级,
     62                             //但是每次改变年级时都会从服务器加载,有点消耗服务器资源
     63                             clazzId.getStore().load({
     64                                 params: {'gid': nv},    //参数
     65                                 callback: function(records, operation, success) { //加载完成调用的函数
     66                                     //添加一个所有选项
     67                                     clazzId.getStore().insert(0, {id: 0, name: '所有' });
     68                                     clazzId.setValue(0); //设置默认第一个
     69                                 }
     70                             });
     71                         }
     72                     }
     73                 }
     74 
     75             });
     76 
     77             /**
     78              * 班级列表
     79              */
     80             Ext.create('Ext.form.ComboBox', {
     81                 renderTo: Ext.getBody(),
     82                 id: 'clazzId',
     83                 displayField: 'name',
     84                 valueField: 'id',
     85                 editable: false,
     86                 readonly: true,
     87                 allowBlank: true,
     88                 fieldLabel: '选择年级',
     89                 margin: '50 10 0 0',
     90                 labelAlign: 'right',
     91                 triggerAction: 'all',
     92                 queryMode: 'local',     //本地加载模式
     93                 store: Ext.create('Ext.data.JsonStore', {   //Store数据仓库
     94                     fields: [
     95                         {name: 'id'},
     96                         {name: 'name'}
     97                     ],
     98                     autoLoad: false,    //设置第二级不自动加载
     99                     proxy: {
    100                         type: 'ajax',
    101                         url: 'student?method=getClazzList',
    102                         reader: {
    103                             type: 'json',
    104                             root: 'content'
    105                         }
    106                     }
    107                 })
    108             });
    109         });
    110 
    111     </script>
    112 </head>
    113 <body>
    114 
    115 </body>
    116 </html>

    上面的代码还有一个问题就是每次都会从服务端加载班级列表,会消耗服务端资源,这对于大型系统来说还是应该优化下的,于是我将数据加载到本地,每次用的时候就去取,整个过程只会向服务端发送两次请求。注意看注释部分!

      1 <%--
      2   
      3   @author bojiangzhou
      4   @date 2016/8/7
      5 --%>
      6 <%@ page contentType="text/html;charset=UTF-8"  language="java" %>
      7 <%@ page isELIgnored="false" %>
      8 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      9 <html>
     10 <head>
     11     <title>Combobox</title>
     12     <link rel="stylesheet" href="js/ext/resources/css/ext-all.css" />
     13     <script type="text/javascript" src="js/ext/ext-all.js"></script>
     14     <script>
     15         Ext.onReady(function () {
     16 
     17             var clazzList = {}; //班级列表
     18 
     19             /**
     20              * 年级列表
     21              */
     22             Ext.create('Ext.form.ComboBox', {
     23                 renderTo: Ext.getBody(),
     24                 id: 'gradeId',
     25                 displayField: 'name',
     26                 valueField: 'id',
     27                 editable: false,
     28                 readonly: true,
     29                 allowBlank: true,
     30                 fieldLabel: '选择年级',
     31                 margin: '50 10 0 0',
     32                 labelAlign: 'right',
     33                 queryMode: 'local',     //本地查询,配置这个属性,在第一次点击下拉列表的时候就不会从服务端加载数据了
     34                 triggerAction: 'all',
     35                 store: Ext.create('Ext.data.JsonStore', {   //Store数据仓库
     36                     fields: [
     37                         {name: 'id'},
     38                         {name: 'name'}
     39                     ],
     40                     autoLoad: true,     //第一级列表设置自动加载
     41                     proxy: {            //通过ajax代理加载数据
     42                         type: 'ajax',
     43                         url: 'student?method=getGradeList',
     44                         reader: {
     45                             type: 'json',
     46                             root: 'ret'
     47                         }
     48                     },
     49                     listeners: {        //注意是store的监听器
     50                         'load': function (store, gRecords) { //store的load事件
     51 
     52                             //在Store的load事件中,加载班级的数据,返回成功后进行一些处理
     53                             Ext.getCmp("clazzId").getStore().load({
     54                                 callback: function(records, operation, success) {   //加载成功返回后调用的函数
     55                                     //将年级全部加载出来放到全局中
     56                                     for(var i = 0;i < records.length;i++){
     57                                         var gid = records[i].data['gid'];   //获取班级所属的年级id
     58                                         if(!clazzList[gid]){
     59                                             clazzList[gid] = [];    //数组用于存放班级
     60                                             clazzList[gid].push({id:0, name: '所有'});    //添加一个所有选项
     61                                         }
     62 
     63                                         clazzList[gid].push(records[i]);    //将record添加到该年级的数组下
     64                                     }
     65 
     66                                     //要先加载后在设置默认值,由于异步加载,change事件可能会不起作用。
     67                                     //设置年级的第一个值为默认值
     68                                     Ext.getCmp("gradeId").setValue(gRecords[0]);    //注意是外部的gRecords
     69                                 }
     70                             });
     71                         }
     72                     }
     73                 }),
     74                 listeners: {        //这是ComboBox的监听器
     75                     'change': function(o, nv){  //change事件
     76                         if(nv){
     77                             var clazzId = Ext.getCmp("clazzId");
     78                             clazzId.getStore().removeAll();// 清空已加载列表
     79                             clazzId.reset();// 清空已存在结果
     80 
     81                             if(clazzList[nv]){
     82                                 //发生change事件后,从班级列表中取出该年级下的班级添加到班级store中
     83                                 clazzId.getStore().insert(0,clazzList[nv]);
     84                                 clazzId.setValue(0);    //设置第一个值默认,即"所有"
     85                             }
     86                         }
     87                     }
     88                 }
     89 
     90             });
     91 
     92             /**
     93              * 班级列表
     94              */
     95             Ext.create('Ext.form.ComboBox', {
     96                 renderTo: Ext.getBody(),
     97                 id: 'clazzId',
     98                 displayField: 'name',
     99                 valueField: 'id',
    100                 editable: false,
    101                 readonly: true,
    102                 allowBlank: true,
    103                 fieldLabel: '选择年级',
    104                 margin: '50 10 0 0',
    105                 labelAlign: 'right',
    106                 triggerAction: 'all',
    107                 queryMode: 'local',     //本地加载模式
    108                 store: Ext.create('Ext.data.JsonStore', {   //Store数据仓库
    109                     fields: [
    110                         {name: 'id'},
    111                         {name: 'gid'},
    112                         {name: 'name'}
    113                     ],
    114                     autoLoad: false,    //设置第二级不自动加载
    115                     proxy: {
    116                         type: 'ajax',
    117                         url: 'student?method=getClazzList',
    118                         reader: {
    119                             type: 'json',
    120                             root: 'content'
    121                         }
    122                     }
    123                 })
    124             });
    125 
    126 
    127         });
    128 
    129     </script>
    130 </head>
    131 <body>
    132 
    133 </body>
    134 </html>

    三、再说说triggerAction

    下面是文档对triggerAction的说明,刚开始不怎么明白这个属性的用途,只知道设置为all的时候能查询出数据来,设置成query的时候就查不出来了....

    后来看到一本书上的例子才对它的用法理解了,triggerAction一般来说会和allQuery、queryParam两个属性配合使用,而且一般combobox是可编辑的,这几个参数是用于输入查询的。

    在triggerAction:'all'的时候,点击下拉列表的时候会根据allQuery的值查询所有相关的数据,queryParam和allQuery可以理解成键和值关系。

    比如配置:queryParam:'grade', allQuery:'一年级', triggerAction:'all',点击下拉列表时,注意是点击下拉列表的时候,就会向后台请求,并带上参数:grade='一年级',然后后台就可以根据这组参数查询该年级下的班级返回来。

    如果你在combo中输入值,且配置了minChars,比如:minChars:3,则在你输入的字符数大于3的时候就会自动向后台发送请求,并带上参数:grade='你输入的值',然后查询。

    设置triggerAction:'query'的时候,在点击下拉列表的时候,发送的参数就是grade='你输入的值',如果没有输入,相当于发送grade=''。

  • 相关阅读:
    五种常见的 PHP 设计模式(收藏)
    写年度工作总结
    关于window.open和window.showdialog返回值的问题
    50个令人叹为观止的JavaScript应用站点[转]
    10大免费FLV播放器下载[转]
    6个去掉图片上的文字的技巧实用简单
    mysql命令大全(转)
    10款替代Windows Media Player的播放器
    Editplus FTP远程访问Ubuntu
    C++ 元编程 Meta Programming
  • 原文地址:https://www.cnblogs.com/chiangchou/p/extjs-combo.html
Copyright © 2011-2022 走看看