ExtJS4组件化编程:动态加载、面向对象与Direct

作者:技术拾光时间:2026-06-12 08:27:07浏览:119

在ExtJS4中通过Ext.define定义类,使用xtype实现组件动态加载,结合Direct模式完成数据传递。重构登录窗口包含三个核心文件:入口页面、继承Ext.Window的登录窗口和表单面板。表单面板采用xtype动态实例化,通过api配置指定后端方法。注意DirectProvider需在类定义前注册,api属性由上层组件传入避免丢失。表单实现回车跳

在ExtJS4中,官方推荐使用Ext.define定义类,再通过xtype实现组件的动态加载。这种约定既能保持代码的清晰分层,也便于后续维护。最近重构了一个登录窗口,按照这个思路执行,确实顺手不少。不过有些细节仍然值得推敲,这里把完整代码贴出来并配上注释,大家一起看看其中的门道。

整个方案基于 Ext + .Net,数据通过 Direct 模式传递。三个核心文件如下:

Default.aspx —— 入口页面

页面加载时先显示一张加载提示图,等 Ext 环境就绪后,实例化登录窗口并显示。这里使用了 Ext.define 定义的类,窗口直接 new 出来调用 show(),未走传统的 renderTo 方式。

复制代码 代码如下:
ExtJS学习
ExtJS4组件化编程:动态加载、面向对象与Direct正在加载...

Ext.app.LoginDialog.js —— 登录窗口本体

这个类继承自 Ext.Window,整个窗口采用 layout: 'fit' 将内容撑满,内部只放了一个表单面板。关键点在于通过 xtype: 'LoginFormPanel' 动态加载子组件,并且在 api 配置中指定后端方法 MyApp.ChcekLogin.Check。这样一来,表单提交时自动走 Direct 调用,无需手动拼接 URL。

复制代码 代码如下:
//LoginDialog类,继承Ext.Window,上层对象使用new Ext.app.LoginDialog().show()动态实例化并显示。 Ext.define('Ext.app.LoginDialog',{ extend:'Ext.Window', title: '登陆', plain: true, closable: false, closeAction: 'hide', width: 400, height: 300, layout: 'fit', border: false, modal: true, //使用xtype: 'LoginFormPanel'动态实例化Ext.app.LoginFormPanel,并使用api参数指定load和submit的服务器端方法。本例中只有submit items: {itemId: 'loginFormPanel',xtype: 'LoginFormPanel',api: {submit: MyApp.ChcekLogin.Check}} });

Ext.app.LoginFormPanel.js —— 表单逻辑核心

表单面板定义得较为详细,包括三个输入框(用户名、密码、验证码)以及验证码图片和提示文字。以下几个设计点值得关注:

  • Direct Provider 的注册时机:必须在 Ext.define 之前完成,否则 initComponent 阶段会因为找不到 api 而报错。代码中放在了类定义之前,这是一个常见陷阱。
  • API 配置的传递:如果在 Ext.define 或者 Ext.apply 中直接写死 api,实例化后往往会丢失,导致提交时提示缺少 url。解决方法是将 api 交给上层组件通过 config 传入,即 LoginDialog 中 items 对象里的 api 属性。该问题在 Ext4 中较为常见,如有更好的思路欢迎交流。
  • 按键事件逻辑:三个输入框均监听 keypress,回车后自动跳到下一个字段,最后一个输入框回车则直接提交表单。这里将 scope 留空,让函数内部通过 this 找到正确的控件链。
  • 提交与重置按钮:submit 方法先验证表单有效性,然后通过 this.getForm().submit() 提交,成功后跳转 main.htm,失败则重置表单并显示错误消息。重置按钮直接调用 form.reset() 清空所有字段。
复制代码 代码如下:
//指定远程调用的Provider,注意不能在initComponent中指定,因为config属性设置是在initComponent之前,会报api找不到错误 Ext.direct.Manager.addProvider(Ext.app.REMOTING_API); //loginForm类,继承Ext.form.FormPanel,使用alias注册至ComponentMgr,上层对象使用xtype:LoginFormPanel动态实例化。 //form的submit()方法使用Direct提交,上层对象实例化本类的时候使用config中的api属性指定服务器端方法。 //很奇怪的是不能在Ext.define或者Ext.apply中指定api属性,指定了实例化之后也会丢失,然后报url参数没有的错误,只能在上层对象实例化本类得时候使用config中的api属性指定api //如果在这里使用原始的new方法指定api也可以,是ext4中的问题?有谁知道的发mail告诉我,万分感谢~~ //使用Ext.define定义本类,定义中使用initComponent指定实例化之前需要执行的操作。 //按面向对象编程思想,本类不调用任何上层对象,方法中不指定scope: this Ext.define('Ext.app.LoginFormPanel',{ extend:'Ext.form.FormPanel', initComponent: function(){ //初始化部分需要完成的功能 //alert("Ext.form.FormPanel 开始加载……"); //貌似Ext.apply得来的属性和在Ext.define中定义的没什么区别,为什么要用这个呢?谁来教教我? Ext.apply(this, { //labelAlign: 'left' }); this.callParent(); }, alias:'widget.LoginFormPanel', labelAlign: 'left', buttonAlign: 'center', bodyStyle: 'padding:5px', frame: true, labelWidth: 80, items: [ { xtype: 'textfield', name: 'txt1', fieldLabel: '用户名称', allowBlank: false, anchor: '90%', enableKeyEvents: true, listeners: { keypress: function (field, e) { if (e.getKey() == 13) { this.nextSibling().focus(); } } //keypress } }, { xtype: 'textfield', inputType: 'password', name: 'txt2', fieldLabel: '用户密码', allowBlank: false, anchor: '90%', enableKeyEvents: true, listeners: { keypress: function (field, e) { if (e.getKey() == 13) { this.nextSibling().focus(); } } //keypress } }, { xtype: 'textfield', name: 'txt3', fieldLabel: '验证码', allowBlank: false, anchor: '90%', mixLength: 6, maxLength: 6, enableKeyEvents: true, listeners: { keypress: function (field, e) { if (e.getKey() == 13) { this.ownerCt.submit(); } } //keypress } }, { xtype: 'panel', height: 100, html: '
ExtJS4组件化编程:动态加载、面向对象与Direct
', border: false }, { xtype: 'panel', height: 30, html: '
*如果图片不清晰请单击图片更换图片
', border: false } ], //items buttons: [ { text: '确定', handler: function () { this.findParentByType('LoginFormPanel').submit(); }}, //面向本对象编程,这里不要加入 scope: this,否则function会指定到window上面 { text: '重置', handler: function () { this.findParentByType('LoginFormPanel').form.reset(); } } ], submit: function () { if (this.getForm().isValid()) { this.getForm().submit({ success: function (form, action) { window.location = "main.htm"; }, failure: function (form, action) { //使用form参数访问原submit的form form.reset(); //使用action.result访问结果集 Ext.MessageBox.alert('登陆失败', action.result.data); } }) } } });

整个过程在注释中已详细说明,如果有更好的实践或遇到类似问题,欢迎在下方讨论交流。