ExtJS4.2 - 从 Hello World 到 自定义组件 - 01

ExtJS4.2 - 从 Hello World 到 自定义组件 - 01

经验、概述、项目搭建、国际化、HelloWorld、布局

                

                ——少走弯路,简单才是王道       

1. 写在前面

我接触 ExtJS 已有两年多时间,不过之前一直用 ExtJS3 ,近几日因为工作需要,才开始使用 ExtJS4 ,鉴于目前手头工作不多,故将一些感受写下,希望能对其他朋友有所帮助。

我在 2011 年前都没有系统的使用过 WEB 前端 UI 框架,其实那会儿, dwr  extjs jquery-ui 都是交替红火着,不过我是个懒惰而又保守的人,加之自己之前也用 html+css+jsavascript+el+tag  自己写过一些东西,也就没去碰这些东西,只是出于项目需要,零散的用过一些 UI 组件,如日历组件、树组件等。

2011 年,作为一个项目的技术架构师,应客户的要求,前端必须采用 ExtJS ,于是我就硬着头皮上了。初次接触 ExtJS 的感觉,是完全颠覆了传统 WEB 前端开发方式, JSP页面上空空如也,内容都在 js 文件中,让人大脑发蒙。从  Hello World  一路写下去,布局、单表维护、树、选择型弹窗、复杂 UI ,写了两个星期,才算适应了这种开发方式,终于恍然大悟,明白应该如何使用 ExtJS4 进行 WEB 前端开发。

2013 年初,本来想接触 ExtJS4 ,然而,另一个项目要求前端必须采用 JQuery Easy UI,然后就把 ExtJS4 丢到一边去了。在此我不得不赞赏 JQuery Easy UI 这个 UI 框架,可谓小身材大智慧,几十 MB 的体积,却包含了强大的功能。我只花了两天,就搭出了框架:分页搜索、查删增改、多选型下拉列表、树型下拉列表、表格型下拉列表、自定义验证、菜单、导航、布局、复杂 UI ;并且 JQuery Easy UI 的开发模式也比较吻合传统WEB 前端开发方式,我对它非常认可,因此打算放弃 ExtJS 了。不过在此我还是必须指出,与 Ext 相比, JQuery Easy UI 在稳定性,界面的专业化程度方面,还是稍逊一筹,不过,它依然是非常好的一个 WEB 前端 UI 组件框架,本人大力推荐!

几天前,由于一个项目的客户要求采用 ExtJS4 ,于是我终于捡起这个东西,起初发现它的代码与 ExtJS3 比较,似乎改变很大,但是深入进去看看,其实架构还是在那里,只是很多类、组件被重构,但是代码更精简了一些。在呈现后的页面上审查元素,发现元素渲染方面也有了优化。比如一个表格的单元格,之前包了几层 div ,现在就是 1 td  1  div 。由于有了 ExtJS3 的基础,因此花了 3 天,我就从 HelloWorld 开始,一步步做到封装了一个查删增改的组件。

在此我想分享一下自己使用 ExtJS 做开发的一点经验:

1. 首先有心理准备,从传统 WEB 页面开发方式转为使用 ExtJS 方式,类似于从   开发转为  Java  开发, ExtJS 方式是面向对象、组件化的, JSP 文件只用于引入资源,页面上呈现的任何元素,都是 ExtJS 的组件,必须一个个创建,然后拼装组合。

  一般而言,我们页面上的元素是这样组织的

     视图 

        面板

            组件

此外,一个弹出窗口的元素则是这样组织的

     窗口

        面板

            组件

2. 此外,如果希望利用 ExtJS 的方便和美观,却又指望它速度快,那是不现实的。友情提示:谷歌浏览器解析 ExtJS 是最快的,最差的是 IE 浏览器。

3. 开发第一步,对 ExtJS4 做一个概览,也就是说,了解它是什么、适应于哪些场景、整体结构是怎样的,有了这个全局观念,开发中就心中有数。千万不要一开始就让自己纠结于细节中。

4. 然后应该开始着手写程序,从  Hello World  开始,不要嫌简陋,一步一步来,目的是大致了解使用 ExtJS4 开发的方式。不至于说起来头头是道,一动手就抓瞎。不过需要注意的是, ExtJS4  demo ,多是在  Ext.onReady  中一路写下来,初学者看了,往往写了一个页面,到下一个页面,就不知道如何把这个页面的东西移过去。因此,我建议采用代码切割的方式,更容易从全局把握。

5. 布局、表格、表单、分页及查删增改、树型及表格型下拉列表、菜单、导航,这些都一一写过,就会有所感觉

6. 之后可以开始写自定义类、组件,扩展 Ext 的一些东西

7. 通过 ExtJS4  API 了解常用类、组件,了解其属性、方法、事件

可以看到,一直到 7 ,才开始进入 ExtJS4 的细节了解,我再次强调,一定要从全局开始,逐步深入局部细节,并且,作为开发人员,绝大部分细节不是你需要了解的,开发中需要的、常用的,才需要认真研究一番。

2. 开发前

2.1. 下载

1.下载ext-4.2.1-gpl 

http://cdn.sencha.com/ext/gpl/ext-4.2.1-gpl.zip

2.下载extjs4 帮助文档

3.下载  org.json.jar

2.2. 配置 tomcat  server.xml 文件

主要是配置: URIEncoding  , 防止 get 访问时中文参数出错

<Connector  URIEncoding="UTF-8"  connectionTimeout= "20000"  port= "8080" protocol= "HTTP/1.1"  redirectPort= "8443" />

2.3. 项目框架搭建

1.Eclipse -  新建动态 WEB 项目( test 

2.将  org.json.jar  加入  WEB-INF/lib  

3.将 ext-4.2.1-gpl 引入项目

4.书写过滤器处理字符集、国际区域;书写监听器处理共享属性

5.书写 servlet 处理查删增改

6.书写 all_zh_CN.js 文件处理国际化公共资源

7.书写 imp.jsp 文件处理前端共享资源

1.1. 项目结构

           

2.4. 过滤器代码

package  test.common;

import  java.io.IOException;

import  javax.servlet.Filter;

import  javax.servlet.FilterChain;

import  javax.servlet.FilterConfig;

import  javax.servlet.ServletException;

import  javax.servlet.ServletRequest;

import  javax.servlet.ServletResponse;

import  javax.servlet.http.HttpServletRequest;

import  javax.servlet.http.HttpServletResponse;

public   class  EncodingFilter  implements  Filter {

     private  String         encoding ; // 编码,默认:utf-8

     public  EncodingFilter() {

    }

     public   void  destroy() {

    }

     public   void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  throws IOException, ServletException {

         // 编码    

     request.setCharacterEncoding( encoding );

        response.setCharacterEncoding( encoding );

         try  {

            ((HttpServletResponse) response).setContentType( "text/html;charset="  + encoding );

        }  catch  (Exception ignore) {

        }

         // 国际区域

        String strLocale =  null ;

         //  取会话中的语言版本

        strLocale = (String)((HttpServletRequest)request).getSession().getAttribute("strLocale" );

         //  如果会话中没有指定,则取浏览器的语言版本

         if  (strLocale ==  null ) {

            strLocale = request.getLocale().toString();

        }

        request.setAttribute( "strLocale" , strLocale);

        chain.doFilter(request, response);

    }

     public   void  init(FilterConfig fConfig)  throws  ServletException {

         encoding  = fConfig.getInitParameter( "encoding" );

         encoding  =  encoding == null ? "UTF-8" : encoding .trim();

         encoding  =  "" .equals( encoding )? "UTF-8" : encoding ;

    }

}

2.5. 监听器代码

package  test.common;

import  javax.servlet.ServletContext;

import  javax.servlet.ServletContextEvent;

import  javax.servlet.ServletContextListener;

public   class  ApplicationListener  implements  ServletContextListener {

public  ApplicationListener() {

    }

public   void  contextInitialized(ServletContextEvent arg0) {

     ServletContext application = arg0.getServletContext();

     application.setAttribute( "webRoot" , application.getContextPath()); // WEB应用根目录

     application.setAttribute( "pageSize" , 20); // 分页尺寸

    }

public   void  contextDestroyed(ServletContextEvent arg0) {

    }

}

2.6. 查删增改 Servlet 代码

package  test.action;

import  java.io.IOException;

import  java.io.PrintWriter;

import  java.util.Map;

import  java.util.Map.Entry;

import  java.util.Set;

import  javax.servlet.ServletException;

import  javax.servlet.http.HttpServlet;

import  javax.servlet.http.HttpServletRequest;

import  javax.servlet.http.HttpServletResponse;

import  org.json.JSONArray;

import  org.json.JSONException;

import  org.json.JSONObject;

public   class  TestAction  extends  HttpServlet {

private   static   final   long   serialVersionUID  = 1L;

private   static  JSONArray  datas ;

     public  TestAction() {

         super ();

iniDatas(); // 初始化数据

    }

protected   void  doGet(HttpServletRequest request, HttpServletResponse response) throws  ServletException, IOException {

myProcess(request, response);

}

protected   void  doPost(HttpServletRequest request, HttpServletResponse response) throws  ServletException, IOException {

myProcess(request, response);

}

// 处理入口

private   void  myProcess(HttpServletRequest request, HttpServletResponse response) throws  ServletException, IOException {

// 处理类型

String myProcessType = request.getParameter( "myProcessType" );

myProcessType = myProcessType== null ? "0" :myProcessType.trim();

myProcessType =  "" .equals(myProcessType)? "0" :myProcessType;

if ( "0" .equals(myProcessType)) {

myProcess_pageQuery(request, response); // 分页搜索

}  else   if ( "1" .equals(myProcessType)) {

myProcess_save(request, response); // 添加或更新

}  else   if ( "2" .equals(myProcessType)) {

myProcess_delete(request, response); // 删除

}  else   if ( "9" .equals(myProcessType)) {

myProcess_submit(request, response); // 其他 - 测试表单提交

}

}

/** 分页搜索

 * 

 * 输出 JSON对象,结构

 *  {

 *  total:  111, // 总行数

 *  root:  // 数据

 *  [

 *  {

 *  id: "记录ID;自增型",

 *  name: "姓名",

 *  sex: "性别;F|M",

 *  tel: "电话",

 *  addr: "地址",

 *  email: "Email"

 *  },

 *  ...

 *  ]

 *  }

 */

private   void myProcess_pageQuery(HttpServletRequest request, HttpServletResponse response) throws  ServletException, IOException {

// 重写 Ext Store 的 beforload 事件传递的自定义参数

String name = request.getParameter( "name" );

String tel = request.getParameter( "tel" );

// Ext Store Proxy 自动传递的分页参数

int  start = 1;

int  limit = 0;

try  {

start = Integer.parseInt(request.getParameter( "start" ));

limit = Integer.parseInt(request.getParameter( "limit" ));

}  catch  (Exception ignore) {

}

JSONObject joAll = getDatas(name, tel, start, limit);

PrintWriter out = response.getWriter();

out.println(joAll);

}

/** 添加或更新

 * 

 * 输出 JSON对象,结构

 *  {

 *  success:  true|false, // 是否成功

 *  msg:  "消息"

 *  }

 */

private   void myProcess_save(HttpServletRequest request, HttpServletResponse response)  throws ServletException, IOException {

String id = request.getParameter( "id" );

String sex = request.getParameter( "sex" );

String name = request.getParameter( "name" );

String tel = request.getParameter( "tel" );

String addr = request.getParameter( "addr" );

String email = request.getParameter( "email" );

id = id== null ? "" :id.trim();

PrintWriter out = response.getWriter();

JSONObject jo =  new  JSONObject();

String result =  null ;

if ( "" .equals(id)) {

result = add(name, sex, tel, addr, email);

}  else  {

result = upd(id, name, sex, tel, addr, email);

}

try  {

if ( "OK" .equals(result)) {

jo.put( "success" ,  true );

}  else  {

jo.put( "success" ,  false );

jo.put( "msg" , result);

}

}  catch  (JSONException ignore) {

}

out.println(jo.toString());

}

/** 删除

 * 

 * 输出 JSON对象,结构

 *  {

 *  success:  true|false, // 是否成功

 *  msg:  "消息"

 *  }

 */

private   void myProcess_delete(HttpServletRequest request, HttpServletResponse response) throws  ServletException, IOException {

String ids = request.getParameter( "ids" );

PrintWriter out = response.getWriter();

JSONObject jo =  new  JSONObject();

String result = del(ids);

try  {

if ( "OK" .equals(result)) {

jo.put( "success" ,  true );

}  else  {

jo.put( "success" ,  false );

jo.put( "msg" , result);

}

}  catch  (JSONException ignore) {

}

out.println(jo.toString());

}

/** 其他  -  测试表单提交

 * 

 * 输出 JSON对象,结构

 *  {

 *  success:  true|false, // 是否成功

 *  msg:  "消息"

 *  }

 */

private   void myProcess_submit(HttpServletRequest request, HttpServletResponse response) throws  ServletException, IOException {

Map<String,Object> map = request.getParameterMap();

StringBuffer sb =  new  StringBuffer();

Set<Entry<String,Object>> ets = map.entrySet();

Object[] vals =  null ;

for (Entry<String,Object> et:ets) {

sb.append(et.getKey()).append( ": " ).append(joinAry((Object[])et.getValue())).append( "<br/>" );

}

PrintWriter out = response.getWriter();

JSONObject jo =  new  JSONObject();

try  {

jo.put( "success" ,  true );

jo.put( "msg" , sb.toString());

}  catch  (JSONException ignore) {

}

out.println(jo.toString());

}

// 添加;返回 OK 表示成功,否则为失败消息

private  String add(String name, String sex, String tel, String addr, String email) {

name = name== null ? "" :name.trim();

tel = tel== null ? "" :tel.trim();

sex = sex== null ? "" :sex.trim();

sex =  "F" .equals(sex)? "F" : "M" ;

addr = addr== null ? "" :addr.trim();

email = email== null ? "" :email.trim();

if ( "" .equals(name) || name.length()>20 || 

"" .equals(tel) || !tel.matches( "^\\d{10,12}$" )  || 

"" .equals(email) && !email.matches( "\\w+@\\w+[.][a-z]+" ) || 

addr.length()>50 ) {

return   "非法操作" ;

}

int  len =  datas .length();

JSONObject jo =  new  JSONObject();

try  {

jo.put( "id" , len+1);

jo.put( "name" , name);

jo.put( "sex" , sex);

jo.put( "tel" , tel);

jo.put( "addr" , addr);

jo.put( "email" , email);

}  catch  (JSONException ignore) {

}

datas .put(jo);

return   "OK" ;

}

// 更新;返回 OK 表示成功,否则为失败消息

private String upd(String id, String name, String sex, String tel, String addr, String email) {

name = name== null ? "" :name.trim();

tel = tel== null ? "" :tel.trim();

sex = sex== null ? "" :sex.trim();

sex =  "F" .equals(sex)? "F" : "M" ;

addr = addr== null ? "" :addr.trim();

email = email== null ? "" :email.trim();

if ( "" .equals(name) || name.length()>20 || 

"" .equals(tel) || !tel.matches( "^\\d{10,12}$" )  || 

"" .equals(email) && !email.matches( "\\w+@\\w+[.][a-z]+" ) || 

addr.length()>50 ) {

return   "非法操作" ;

}

JSONObject jo =  null ;

int  len =  datas .length();

String id2 =  null ;

boolean  flag =  false ;

for  ( int  i = 0; i < len; i++) {

try  {

jo =  datas .getJSONObject(i);

id2 = jo.isNull( "id" )? "" :jo.getString( "id" );

id2 = id2== null ? "" :id2.trim();

if (id2.equals( "" ) || id2.equals( "D" ) || !id2.equals(id)) {

continue ;

}

jo.put( "name" , name);

jo.put( "sex" , sex);

jo.put( "tel" , tel);

jo.put( "addr" , addr);

jo.put( "email" , email);

flag =  true ;

break ;

}  catch  (JSONException ignore) {

continue ;

}

}

return  flag? "OK" : "没有该数据{" +id+ "}" ;

}

// 删除;返回 OK 表示成功,否则为失败消息

private  String del(String ids) {

if ( "" .equals(ids)) {

return   "非法操作" ;

}

ids =  "," +ids+ "," ;

JSONObject jo =  null ;

int  len =  datas .length();

String id2 =  null ;

boolean  flag =  false ;

for  ( int  i = 0; i < len; i++) {

try  {

jo =  datas .getJSONObject(i);

id2 = jo.isNull( "id" )? "" :jo.getString( "id" );

id2 = id2== null ? "" :id2.trim();

if (id2.equals( "" ) || id2.equals( "D" ) || ids.indexOf( "," +id2+ "," )<0) {

continue ;

}

jo.put( "id" ,  "D" ); // 标志删除

flag =  true ;

}  catch  (JSONException ignore) {

continue ;

}

}

return  flag? "OK" : "没有这些数据{" +ids+ "}" ;

}

/* 分页搜索

 * 

 * @param name 姓名

 * @param tel 电话

 * @param start 起始索引号

 * @param limit 结束索引号

 * 

 * @return JSON对象,结构

 *  {

 *  total:  111, // 总行数

 *  root:  // 数据

 *  [

 *  {

 *  id: "记录ID;自增型",

 *  name: "姓名",

 *  sex: "性别;F|M",

 *  tel: "电话",

 *  addr: "地址",

 *  email: "Email"

 *  },

 *  ...

 *  ]

 *  }

 */

private  JSONObject getDatas(String name, String tel,  int  start,  int  limit) {

name = name== null ? "" :name.trim();

int  len =  datas .length();

JSONObject jo =  null ;

String name2 =  null ;

String tel2 =  null ;

JSONArray ja =  new  JSONArray();

String id2 =  null ;

for ( int  i=0;i<len;i++) {

try  {

jo =  datas .getJSONObject(i);

id2 = jo.isNull( "id" )? "" :jo.getString( "id" );

if (id2.equals( "" ) || id2.equals( "D" )) {

continue ;

}

if (! "" .equals(name)) {

name2 = jo.isNull( "name" )? "" :jo.getString( "name" );

name2 = name2== null ? "" :name2.trim();

if (name2.indexOf(name)<0) {

continue ;

}

}

if (! "" .equals(tel)) {

tel2 = jo.isNull( "tel" )? "" :jo.getString( "tel" );

tel2 = tel2== null ? "" :tel2.trim();

if (tel2.indexOf(tel)<0) {

continue ;

}

}

ja.put(jo);

}  catch  (JSONException ignore) {

}

}

limit = limit<=0?20:limit;

int  total = ja.length();

start = start>=total?total-1:(start<0?0:start);

int  end = start+limit-1;

end = end>=total?total:(end<start?start:end);

JSONArray ja2 =  new  JSONArray();

for ( int  i=0;i<total;i++) {

if (i>=start && i<=end) {

try  {

ja2.put(ja.getJSONObject(i));

}  catch  (JSONException ignore) {

}

}

}

JSONObject joAll =  new  JSONObject();

try  {

joAll.put( "total" , total);

joAll.put( "root" , ja2);

}  catch  (JSONException ignore) {

}

return  joAll;

}

/*  初始化数据,数据结构

 *  [

 *   {

 *   id: "记录ID;自增型",

 *   name: "姓名",

 *   sex: "性别;F|M",

 *   tel: "电话",

 *   addr: "地址",

 *   email: "Email"

 *   },

 *   ...

 *  ]

 */

private   void  iniDatas() {

if ( datas == null ) {

datas  =  new  JSONArray();

JSONObject jo =  null ;

for ( int  i=0;i<3000;i++) {

jo =  new  JSONObject();

try  {

jo.put( "id" , i+1);

jo.put( "name" ,  "无名氏" +(i+1));

jo.put( "sex" , i%4==0? "F" : "M" );

jo.put( "tel" ,  "152" +( int )((99999999-11111111+1)*Math.random()+11111111));

jo.put( "addr" ,  "无名小镇" +( int )((9999-1111+1)*Math.random()+1111)+ "号" );

jo.put( "email" ,  "wuming" +( int )((999999-111111+1)*Math.random()+111111)+"@wuming.com" );

}  catch  (JSONException ignore) {

}

datas .put(jo);

}

}

}

// 辅助方法:数组的 join

private  String joinAry(Object[] ary) {

int  len = ary== null ?0:ary. length ;

if (len==0) {

return   "" ;

}

StringBuffer sb =  new  StringBuffer();

for (Object o:ary) {

sb.append(o).append( ", " );

}

int  ix = sb.lastIndexOf( "," );

if (ix>=0) {

sb.deleteCharAt(ix);

}

return  sb.toString();

}

}

2.7. web.xml 代码

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"  

xmlns= "http://java.sun.com/xml/ns/javaee"  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id= "WebApp_ID"  version= "2.5" >

   <display-name> test</display-name>

   <welcome-file-list>

     <welcome-file> index.jsp </welcome-file>

   </welcome-file-list>

   <!-- 应用程序启动监听器 -->

   <listener>

     <listener-class> test.common.ApplicationListener </listener-class>

   </listener>

   <!-- 查删增改Servlet -->

   <servlet>

     <description></description>

     <display-name> TestAction </display-name>

     <servlet-name> TestAction </servlet-name>

     <servlet-class> test.action.TestAction </servlet-class>

   </servlet>

   <servlet-mapping>

     <servlet-name> TestAction </servlet-name>

     <url-pattern> /test.do </url-pattern>

   </servlet-mapping>

   <!-- 编码过滤器 -->

   <filter>

     <display-name> 编码过滤器,有1个参数可配置,具体见该类说明 </display-name>

     <filter-name> EncodingFilter </filter-name>

     <filter-class> test.common.EncodingFilter </filter-class>

     <init-param>

       <description> 编码 </description>

       <param-name> encoding </param-name>

       <param-value> utf-8 </param-value>

     </init-param>

   </filter>

   <filter-mapping>

     <filter-name> EncodingFilter </filter-name>

     <url-pattern> *.do </url-pattern>

   </filter-mapping>

   <filter-mapping>

     <filter-name> EncodingFilter </filter-name>

     <url-pattern> *.jsp </url-pattern>

   </filter-mapping>

   <filter-mapping>

     <filter-name> EncodingFilter </filter-name>

     <url-pattern> *.html </url-pattern>

   </filter-mapping>

   <filter-mapping>

     <filter-name> EncodingFilter </filter-name>

     <url-pattern> *.htm </url-pattern>

   </filter-mapping>

   <filter-mapping>

     <filter-name> EncodingFilter </filter-name>

     <url-pattern> *.xml </url-pattern>

   </filter-mapping>

   <filter-mapping>

     <filter-name> EncodingFilter </filter-name>

     <url-pattern> *.js </url-pattern>

   </filter-mapping>

   <filter-mapping>

     <filter-name> EncodingFilter </filter-name>

     <url-pattern> *.css </url-pattern>

   </filter-mapping>

</web-app>

2.8. all_zh_CN.js 代码

var  i18n_all = {

title: {

dataList:  "数据表格" ,

addOpe:  "添加操作" ,

editOpe:  "编辑操作" ,

delOpe:  "删除操作" ,

guide:  "第 {0} 步,共 {1} 步"

},

lable: {

},

btn: {

pre:  "上一步" ,

next:  "下一步" ,

add:  "添加" ,

edit:  "编辑" ,

del:  "删除" ,

save:  "保存" ,

reset:  "重置"

},

msg: {

addConfirm:  "是否确认添加" ,

addOK:  "添加成功" ,

addErr:  "添加失败" ,

editConfirm:  "是否确认更新" ,

editOK:  "编辑成功" ,

editErr:  "编辑失败" ,

delConfirm:  "是否确认删除" ,

delOK:  "删除成功" ,

delErr:  "删除失败"

}

};

2.9. imp.jsp 代码

<%@   page   language = "java"  

contentType = "text/html; charset=utf-8"   pageEncoding = "utf-8" %>

<%-- EXT-CSS文件 --%>

< link   rel = "stylesheet"   type = "text/css"  

href = " ${webRoot } /res/extjs-4.2.0/resources/css/ext-all.css"   />

<%-- EXT-JS引导文件(根据模式决定调入 ext_all.js 或 ext_all_dev.js) --%>

< script   type = "text/javascript"  

src = " ${webRoot } /res/extjs-4.2.0/bootstrap.js" ></ script >

<%-- EXT-国际化文件,strLocale 在过滤器中已设置 --%>

< script   type = "text/javascript"  

src = " ${webRoot } /res/extjs-4.2.0/locale/ext-lang- ${strLocale } .js" ></ script >

< script >

// 定义3个变量,用于 .js 文件

var  global_webRoot =  "${webRoot}" ; // WEB应用根目录 - 在监听器中已设置

var  global_pageSize =  "${pageSize}" ; // 分页大小 - 在监听器中已设置

var  global_strLocale =  "${strLocale}" ; // 国际区域 - 在过滤器中已设置

</ script >

< script   type = "text/javascript"  

src = " ${webRoot } /locale/common/all_ ${strLocale } .j" ></ script >

3. Hello World

3.1. 开发

3.1.1. 创建  /locale/test_zh_CN.js 文件

var  i18n_test = {

title: {

test1:  "伊兰.ExtJS4专区"

},

lable: {

},

btn: {

},

msg: {

test1:  "你好!欢迎光临伊兰.ExtJS4专区!"

}

};

3.1.2. 创建  /test01/test.js 文件

var  i18n_my = i18n_test; // 国际化对象,见 /locale/test/test_zh_CN.js 文件

//Ext.application({ // 程序入口;作用类似 Ext.onReady ;用于 MVC 架构

// name : "HelloExt",

// launch : function() {

// var p = getPanel();

//

// Ext.create("Ext.container.Viewport", {

// layout : "fit",

// items : [

// p

// ]

// });

// }

//});

// 入口

Ext.onReady( function (){ // 程序入口,页面加载完后会自动调用 Ext.onReady

// 获取面板

var  p = getPanel();

// 通过视图呈现面板

// Viewport 为顶层容器,以页面 body 元素作为载体,呈现内容

Ext.create( "Ext.container.Viewport" , {

layout :  "fit" , // fit 布局:填满容器

items : [ // Viewport包含的内容

p

]

});

});

// 创建并返回面板

function  getPanel() {

// var p =   new Ext.panel.Panel({ // 普通加载

// // ...

// });

var  p =  Ext.create( "Ext.panel.Panel" , { // 动态加载

title : i18n_my.title.test1,

html : i18n_my.msg.test1

// 如果这个 Panel 不是通过 Viewport 呈现,则可通过 renderTo 指定呈现载体 

//,renderTo: Ext.getBody() // 以页面 body 元素作为呈现载体

//,renderTo: "d01" // 以ID为 d01 的元素作为呈现载体

});

// 返回面板

return  p;

}

3.1.3. 创建  /test01/test.jsp 文件

<%@   page   language = "java"  

contentType = "text/html; charset=utf-8"   pageEncoding = "utf-8" %>

<%@   include   file = "/common/imp.jsp" %> <%-- 共享文件 --%>

<%-- 本页面国际化JS文件 --%>

< script   src = " ${webRoot } /locale/test/test_ ${strLocale } .js" ></ script >

<%-- 本页面功能JS文件 --%>

< script   src = " ${webRoot } / test01/test.js " ></ script >

<%-- <div id="d01"></div> --%>

3.1.4. 浏览

启动服务器,浏览: http://localhost:8080/test/test0 1 /test.jsp


3.2. 讲解

3.2.1. Ext.application  和  Ext.ready

这 2 个方法将在 Ext 加载完毕后自动调用

其中, Ext.application  ExtJS4 引入的新方式,用于 ExtJS4  MVC 架构

3.2.2. Ext.container.Viewport

顶层容器,视图组件,可以包含其他容器类组件

以 body  元素作为呈现载体

主要配置为:

layouot: “布局”,

items: [  成员,通常为面板  ]

3.2.3. Ext.panel.Panel

面板,容器类组件,可以包含其他组件,包括容器类组件

如果不放在视图内,必需用  renderTo  指定呈现载体

主要配置为:

title: “标题”,

width: 500, //  宽度 

height: 300, //  高度

items: [], //  成员

tbar: [], //  工具栏

bbar: [], //  状态栏

buttons: [], //  按钮

listeners: { //  事件监听器

activate: function(panel, eopt) {

//  面板激活时的处理代码, panel 表示面板自身

},

show: function(panel, eopt) {

//  面板呈现时的处理代码, panel 表示面板自身

},

..

}

3.2.4. new ***({})  与  Ext.create( “***”, {});

创建 Ext 对象

区别犹如 java 中的  new ***  与  Class.forName( “***”).newInstance()

也就是说,后者是动态创建,这是 ExtJS4 的亮点,实现了按需加载

4. 布局

Viewport 也好, Panel 也好,在默认情况下,它们只能有 1 个成员,如果有多个成员,只会呈现第一个;那么,如何使它们能拥有和呈现多个成员呢?那么,这就要靠布局来实现。布局,就是用来组织多个成员的。

默认的布局方式,一般是  fit  ,表示成员填充整个容器

4.1. 边界( border 

4.1.1. 概述

如果容器需要划分为几个部分,如上中下,或左右等,凡涉及到上下左右中的,都使用边界布局,这也是最常用的布局

边界布局的名称是  border  ,它将容器划分为 上下左右中 五大块,其中,只有 中 部是必需的

容器内的成员通过  region  属性,指示将自身装载到容器的哪个部分

上、下:分别为  north  、  south ;这两个部分必须且只能规定高度

左、右:分别为  west 、  east ;这两个部分必须且只能规定宽度

中:为  center ;高度和宽度都为自动,自动填充容器剩余的部分

4.1.2. 开发

4.1.2.1. 修改  /locale/test_zh_CN.js 文件

var  i18n_test = {

...,

msg: {

...,

test2_0_1:  "这里是 north" ,

test2_0_2:  "这里是 west" ,

test2_0_3:  " 这里是 center (伊兰.ExtJS4专区.边界布局) " ,

test2_0_4:  "这里是 east" ,

test2_0_5:  "这里是 south"

}

};

4.1.2.2. 创建  /test02/test.js 文件

var  i18n_my = i18n_test; // 国际化对象,见 /locale/test/test_zh_CN.js 文件

// 入口

Ext.onReady( function (){

var  p1 = getTop();

var  p2 = getLeft();

var  p3 = getCenter();

var  p4 = getRight();

var  p5 = getBottom();

Ext.create( "Ext.container.Viewport" , {

layout :  "border" , // 边界布局

items : [

    p1,

    p2,

    p3, // 只有中部是必需的;中部的大小是自动的,不可指定的

    p4,

    p5

]

});

});

// 头部

function  getTop() {

var  p =  Ext.create( "Ext.panel.Panel" , {

html : i18n_my.msg.test2_0_1,

height: 80, // 底部必须且只能指定高度

border:  false ,

region:  "north" // 指示将自身装载到容器的头部

});

return  p;

}

// 左边

function  getLeft() {

var  p =  Ext.create( "Ext.panel.Panel" , {

html : i18n_my.msg.test2_0_2,

width: 200, // 左边必须且只能指定宽度

collapsible:  true , // 面板标题栏将出现一个 >> 图标,点击将折叠或展开面板

split:  true , // 该面板的宽度将可用鼠标调整

region:  "west" // 指示将自身装载到容器的左部

});

return  p;

}

// 中部

function  getCenter() {

var  p =  Ext.create( "Ext.panel.Panel" , {

html : i18n_my.msg.test2_0_3,

bodyStyle:  "border-width:1px 0px 1px 0px" ,

region:  "center" // 指示将自身装载到容器的中部

});

return  p;

}

// 右边

function  getRight() {

var  p =  Ext.create( "Ext.panel.Panel" , {

html : i18n_my.msg.test2_0_4,

width: 120, // 右边必须且只能指定宽度

collapsible:  true , // 面板标题栏将出现一个 >> 图标,点击将折叠或展开面板

region:  "east" // 指示将自身装载到容器的右部

});

return  p;

}

// 底部

function  getBottom() {

var  p =  Ext.create( "Ext.panel.Panel" , {

html : i18n_my.msg.test2_0_5,

height: 40, // 底部必须且只能指定高度

border:  false ,

region:  "south" // 指示将自身装载到容器的底部

});

return  p;

}

4.1.2.3. 创建  /test02/test.jsp 文件

<%@   page   language = "java"  

contentType = "text/html; charset=utf-8"   pageEncoding = "utf-8" %>

<%-- 共享文件 --%>

<%@   include   file = "/common/imp.jsp" %>

<%-- 本页面国际化JS文件 --%>

< script   src = " ${webRoot } /locale/test/test_ ${strLocale } .js" ></ script >

<%-- 本页面功能JS文件 --%>

< script   src = " ${webRoot } /t est02/test.js " ></ script >

4.1.2.4. 浏览

浏览: http://localhost:8080/test/test0 2 /test.jsp

注意左右面板的  <<  和  >>  按钮;注意左面板与中部的分割线


4.2. 手风琴( accordion 

4.2.1. 概述

我们常见页面右边是导航栏,手风琴布局可以实现

手风琴布局的名称是 accordion

每个成员面板都支持展开和折叠。

在任何时间里,只有一个成员面板处于展开状态,其他都被折叠。

注意:只有 Ext.Panels 和所有 Ext.panel.Panel 子项才可以使用 accordion 布局

4.2.2. 开发

4.2.2.1. 修改  /locale/test_zh_CN.js 文件

var  i18n_test = {

...,

msg: {

...,

// 届时将使用 Ext.String.format 方法填充 {0} 参数

test2_1_0:  "这里是 {0} 的导航栏" ,

test2_1_1:  "系统管理模块" ,

test2_1_2:  "权限管理模块" ,

test2_1_3:  "基础数据管理模块" ,

test2_1_4:  "任务调度管理模块" ,

test2_1_5:  "数据同步管理模块"

}

};

4.2.2.2. 创建  /test02/test1.js 文件

var  i18n_my = i18n_test; // 国际化对象,见 /locale/test/test_zh_CN.js 文件

// 入口

Ext.onReady( function (){

var  p1 = getLeft();

var  p2 = getCenter();

Ext.create( "Ext.container.Viewport" , {

layout :  "border" ,

items: [

p1,

p2

]

});

});

// 左边 - 整体

function  getLeft() {

// 获取5个成员面板

var  p1 = getLeft01();

var  p2 = getLeft02();

var  p3 = getLeft03();

var  p4 = getLeft04();

var  p5 = getLeft05();

// 创建并返回手风琴布局面板

var  p =  Ext.create( "Ext.panel.Panel" , {

layout:  "accordion" , // 手风琴布局

title: i18n_my.title.test2_1,

width: 200,

collapsible:  true ,

split:  true ,

region:  "west" ,

items: [

p1,

p2,

p3,

p4,

p5

]

});

return  p;

}

// 左边 - 第 1 个成员

function  getLeft01() {

var  tt = i18n_my.msg.test2_1_1;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 左边 - 第 2 个成员

function  getLeft02() {

var  tt = i18n_my.msg.test2_1_2;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 左边 - 第 3 个成员

function  getLeft03() {

var  tt = i18n_my.msg.test2_1_3;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 左边 - 第 4 个成员

function  getLeft04() {

var  tt = i18n_my.msg.test2_1_4;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 左边 - 第 5 个成员

function  getLeft05() {

var  tt = i18n_my.msg.test2_1_5;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 中部

function  getCenter() {

var  p =  Ext.create( "Ext.panel.Panel" , {

html : i18n_my.msg.test2_0_3,

bodyStyle:  "border-width:1px 0px 1px 0px" ,

region:  "center"

});

return  p;

}

4.2.2.3. 创建  /test02/test1.jsp 文件

<%@   page   language = "java"  

contentType = "text/html; charset=utf-8"   pageEncoding = "utf-8" %>

<%-- 共享文件 --%>

<%@   include   file = "/common/imp.jsp" %>

<%-- 本页面国际化JS文件 --%>

< script   src = " ${webRoot } /locale/test/test_ ${strLocale } .js" ></ script >

<%-- 本页面功能JS文件 --%>

< script   src = " ${webRoot } /test02/test1.js " ></ script >

4.2.2.4. 浏览

浏览: http://localhost:8080/test/test0 2 /test 1 .jsp

点击左边每个子面板,可见其他子面板被这地,被点击的面板则展开


4.3. 卡片( card 

4.3.1. 概述

我们常见向导界面,卡片布局可以实现

卡片布局的名称是  card

在任何时间里,只有一个成员面板处于激活状态,其他都被隐藏。

需要自行开发导航按钮,以导航到不同的成员面板,实现 上一步、下一步 的功能

注意: 由于 card 布局需要配合导航按钮,而 Viewport 不具备 buttons 配置,故不宜直接将 Viewport 配置为 card 布局,而应用一个 Panel 作为 card 布局的总容器,再将这个Panel 加入到 Viewport

4.3.2. 开发

4.3.2.1. 修改  /locale/test_zh_CN.js 文件

var  i18n_test = {

...,

msg: {

...,

test2_2_02:  "现在进行第 {0} 步操作..."

}

};

4.3.2.2. 创建  /test02/test2.js 文件

var  i18n_my = i18n_test; // 国际化对象,见 /locale/test/test_zh_CN.js 文件

var  g_ix = 0; // 索引,决定card布局中,哪个成员面板被显示

// 入口

Ext.onReady( function (){

var  p1 = getLeft();

var  p2 = getCenter();

Ext.create( "Ext.container.Viewport" , {

layout :  "border" ,

items: [

p1,

p2

]

});

});

// 左边 - 整体

function  getLeft() {

// 获取5个成员面板

var  p1 = getLeft01();

var  p2 = getLeft02();

var  p3 = getLeft03();

var  p4 = getLeft04();

var  p5 = getLeft05();

// 创建并返回手风琴布局面板

var  p =  Ext.create( "Ext.panel.Panel" , {

layout:  "accordion" , // 手风琴布局

title: i18n_my.title.test2_1,

width: 200,

collapsible:  true ,

split:  true ,

region:  "west" ,

items: [

p1,

p2,

p3,

p4,

p5

]

});

return  p;

}

// 左边 - 第 1 个成员

function  getLeft01() {

var  tt = i18n_my.msg.test2_1_1;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 左边 - 第 2 个成员

function  getLeft02() {

var  tt = i18n_my.msg.test2_1_2;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 左边 - 第 3 个成员

function  getLeft03() {

var  tt = i18n_my.msg.test2_1_3;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 左边 - 第 4 个成员

function  getLeft04() {

var  tt = i18n_my.msg.test2_1_4;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 左边 - 第 5 个成员

function  getLeft05() {

var  tt = i18n_my.msg.test2_1_5;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: tt,

html : Ext.String.format(i18n_my.msg.test2_1_0, tt),

border:  false

});

return  p;

}

// 中部 - 整体

function  getCenter() {

// 按钮:上一步

var  btnPre = Ext.create( "Ext.button.Button" , {

id:  "btnPre" ,

text: i18n_all.btn.pre,

disabled:  true ,

handler:  function () {

g_ix--;

showPanel();

}

});

// 按钮:下一步

var  btnNext = Ext.create( "Ext.button.Button" , {

id:  "btnNext" ,

text: i18n_all.btn.next,

handler:  function () {

g_ix++;

showPanel();

}

});

// 获取4个成员面板

var  p1 = getCenter01();

var  p2 = getCenter02();

var  p3 = getCenter03();

var  p4 = getCenter04();

// 创建并返回卡片式布局面板

var  p =  Ext.create( "Ext.panel.Panel" , {

id:  "center1" ,

layout:  "card" ,

title: i18n_my.title.test2_2,

region:  "center" ,

items: [

p1,

p2,

p3,

p4

],

buttons: [

btnPre,

btnNext

]

});

return  p;

}

// 中部 - 第 1 个成员

function  getCenter01() {

var  tt = 1;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: Ext.String.format(i18n_all.title.guide, tt,  "4" ),

html : Ext.String.format(i18n_my.msg.test2_2, tt),

border:  false

});

return  p;

}

// 中部 - 第 2 个成员

function  getCenter02() {

var  tt = 2;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: Ext.String.format(i18n_all.title.guide, tt,  "4" ),

html : Ext.String.format(i18n_my.msg.test2_2, tt),

border:  false

});

return  p;

}

// 中部 - 第 3 个成员

function  getCenter03() {

var  tt = 3;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: Ext.String.format(i18n_all.title.guide, tt,  "4" ),

html : Ext.String.format(i18n_my.msg.test2_2, tt),

border:  false

});

return  p;

}

// 中部 - 第 4 个成员

function  getCenter04() {

var  tt = 4;

var  p =  Ext.create( "Ext.panel.Panel" , {

title: Ext.String.format(i18n_all.title.guide, tt,  "4" ),

html : Ext.String.format(i18n_my.msg.test2_2, tt),

border:  false

});

return  p;

}

// 导航到某个面板

function  showPanel() {

// 处理索引号,激活索引号对应的面板

g_ix = g_ix<0?0:(g_ix>3?3:g_ix);

Ext.getCmp( "center1" ).getLayout().setActiveItem(g_ix);

// 根据索引号,设置 上一步、下一步 按钮的可用性

Ext.getCmp( "btnPre" ).setDisabled(g_ix<=0);

Ext.getCmp( "btnNext" ).setDisabled(g_ix>=3);

}

4.3.2.3. 创建  /test02/test2.jsp 文件

<%@   page   language = "java"  

contentType = "text/html; charset=utf-8"   pageEncoding = "utf-8" %>

<%-- 共享文件 --%>

<%@   include   file = "/common/imp.jsp" %>

<%-- 本页面国际化JS文件 --%>

< script   src = " ${webRoot } /locale/test/test_ ${strLocale } .js" ></ script >

<%-- 本页面功能JS文件 --%>

< script   src = " ${webRoot } /test02/test 2 .js " ></ script >

4.3.2.4. 浏览

浏览: http://localhost:8080/test/test0 2 /test 2 .jsp

1.初始时,显示的是 card 布局中第 1 个成员面板, 上一步 按钮被禁用

2.点击 下一步 按钮后,显示第 2 个成员面板, 上一步、下一步按钮都被启用

3.到第 4 步时,由于这是最后一个面板,所以 下一步 按钮被禁用


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值