前言
在上一次http://www.cnblogs.com/aehyok/archive/2013/04/17/3025957.html主要是搭建Ext環境,本次課程主要是通過Ext組件來實現登錄。
開始動手
1.在解決方案資源管理器中選擇Scripts\ExtJS\ux目錄,單擊右鍵選擇添加,新建項,在彈出窗口中選擇Jscript文件,並將名稱修改為login.js(以后的項目的可直接將該文件復制到該目錄)
這里要注意,文件名不能用類的全名做文件名,因為動態加載會根據類名自動找到目錄並加載文件,類名中最后一個小數點后的名稱就是文件名,例如,登錄窗口的類全稱為Ext.ux.Login,而login就是文件名。
如果想要在腳本中使用ExtJS的提示信息,可將書附帶的資源包中的Ext.js文件復制到ExtJS目錄中,復制后,在解決方案資源管理器將Ext.js拖到到login.js文件中,就會生成以下代碼:
/// <reference path="../Ext.js" />
2.現在,先把類的定義寫好,包括父類、單例模式、窗口標題、寬度和高度。窗口的標題為“Ext Js MVC登錄窗口”。寬度和高度暫定為400,到時候再調整。最終代碼如下(Login.js中):
Ext.define("Ext.ux.Login", { extend: "Ext.window.Window", singleton: true, title: 'ExtJs Mvc登錄窗口', width: 400, height: 400 });
3.要考慮窗口應該包含那些配置項了,窗口應是模態的,不能關閉,不能調整大小,關閉模式為隱藏,隱藏模式為偏移等,因而加入以下代碼:
modal: true, closable: false, resizable: false, closeAction: 'hide', hideMode: 'offsets',
4.在窗口的initComponent方法內定義登錄用的控件了。一般的登錄窗口都包含用戶名、密碼和驗證碼3個文本輸入框,還包含有顯示驗證碼的圖片、登錄和重置按鈕。因而需要用到的ExtJS控件包括表單面板、圖片、工具欄、按鈕和文本字段。下面要做的是先定義好表單,在擴展內加入以下代碼:
initComponent: function () { var me = this; me.form = Ext.create(Ext.form.Panel, { }); me.callParent(arguments); }
代碼中,me的作用是將外部作用域中的this對象保存為本地變量,這樣的好處包括,一是,如果this是window等全局變量,就可以將全局變量變成本地變量,提高訪問效率,二是可以讓閉包訪問該對象。這寫法在Ext JS文件中始終貫穿其中,本着拿來主義的精神,好東西應該學一下。
注意create方法中的對象名稱,筆者並沒有使用字符串,這樣就可以直接使用對象,而不需要再去轉換表中找對象,可以提高速度。
調用callParent方法是必須的,不然組件運行會出問題,達不到預期效果。
5.來定義表單配置項了,代碼如下:
border: false, bodyPadding: 5, bodyStyle: "background:#DFE9F6",
代碼中,第一句表示不要邊框,如果喜歡帶有邊框的表單,可以把這項去掉或者修改為true。第二句表示將表單面板向內壓縮5像素,這樣表單內的組件就不會和窗口的內邊框粘在一起,這個可根據個人喜好設置。第三句的作用就是讓表單面板的背景顏色和窗口融合在一起,而不是默認的白色,這還是個人喜好問題。
6.接着加入表單面板的提交地址,這里定為Account/Login,就是Account控制器的Login方法,代碼如下:
url: "Account/Login",
7.因為表單內使用的都是文本字段,因而可以統一做一些定義,如標簽寬度為80,標簽的分隔符為中文冒號,錨固為0,都不允許為空等,代碼如下:
defaultType: "textfield", fieldDefaults: { labelWidth: 80, labelSeparator: ":", anchor: "0", allowBlank: false },
8.接下來是定義字段了,這個簡單,因為默認設置已經定義了幾個配置項,因而余下的就只有字段標簽和名稱。驗證碼特殊點,必須是6位字符,代碼如下:
items: [ { fieldLabel: "用戶名", name: "UserName" }, { fieldLabel: "密碼", name: "Password", inputType: "password" }, { fieldLabel: "驗證碼", name: "Vcode", minLength: 6, maxLength: 6 } }
9.現在要考慮怎么顯示驗證碼圖片,如果直接在表單內加入Image控件,會很難控制圖片的位置,因為最好的方式是先套一個容器。因為Img對象的實例在刷新圖片的時候還要用到,因而最好用一個屬性來指向對象實例,這樣就可以通過該屬性在類的內部訪問到實例了。在創建表單的前面添加以下創建Img對象實例的代碼:
me.image = Ext.create(Ext.Img, { src: "/VerifyCode" });
千萬不要在創建表單后面創建,不然在表單內插入圖片的時候就找不到對象了。
代碼中,驗證碼圖片將VerifyCode控制器生成,這個暫時放下,會在后面討論。
10.還要實現的是單擊圖片刷新驗證碼,但是查API發現Img對象居然沒單擊事件。沒關系,在4.1版本的Ext JS中,修改了事件的定義方式,可以直接為對象生成的HTML元素綁定事件了,只要在監聽事件中加入element配置項就行了,這相當方法。因而可在創建Img實例的配置對象中加入以下代碼:
listeners: { click: me.onRefrehImage, element: "el", scope: me }
代碼中,element配置項中的el就表示要在對象生成的HTML元素中綁定事件,綁定事件為click事件,事件將調用onRefrehImage方法。方法只是簡單的刷新圖片,因而使用Img對象的setSrc方法就可以,使用以下代碼順便完成onRefrehImage方法:
onRefrehImage: function () { this.image.setSrc("/VerifyCode?_dc=" + (new Date()).getTime()); }
代碼很簡單,使用setSrc方法刷新圖片的src就行了,加上時間戳可防止顯示緩存圖片。
好了,可以在表單items里加入驗證碼圖片了,代碼如下:
{ xtype: "container", height: 80, anchor: "-5", layout: "fit", items: [me.image] }
從代碼可以看到,使用容器的作用就是可以使用fit布局來限制圖片的尺寸,這樣布局就容易多了。
還要加入一段提示信息,告知用戶驗證碼不區分大小寫,且如果看不清楚驗證碼圖片,可單擊圖片刷新驗證碼,代碼如下:
{ xtype: "container", anchor: "-5", html: "**驗證碼不區分大小寫,如果看不清楚驗證碼,可單擊圖片刷新驗證碼。" }
11.表單余下的就是添加登錄和重置按鈕了,代碼如下:
dockedItems: [{ xtype: 'toolbar', dock: 'bottom', ui: 'footer', layout: { pack: "center" }, items: [ { text: "登錄", width: 80, disabled: true, formBind: true, handler: me.onLogin, scope: me }, { text: "重置", width: 80, handler: me.onReset, scope: me } ] }]
在這里使用了dockedItems配置項,目的一是因為介紹Ext JS 4的新功能,二是因為使用這個確實挺方便。代碼中定義了一個工具欄,停靠位置由dock配置項決定,在這里是底部(bottom),工具欄的樣式使用了ui配置項定義的footer,也就是原來窗口的底部頁腳工具欄,工具欄的布局將使用居中對齊方式。
登錄按鈕預設為禁用的。formBind配置的作用是只有在表單內輸入符合要求時才能使用該按鈕,這個設計在Ext JS4也是新加入的,很方便,不再需要自己去寫代碼實現這個了。登錄按鈕將調用onLogin方法。重置按鈕很簡單,只是簡單的調用onReset方法。
余下要完成的是onLogin和onReset方法。先來完成簡單onReset方法,基本功能就是重置表單,並將焦點移動到第一個文本字段,也就是用戶名那里,還要刷新驗證碼,代碼如下:
onReset: function () { var me = this; me.form.getForm().reset(); if (me.form.items.items[0]) { me.form.items.items[0].focus(true, 10); } me.onRefrehImage(); }
代碼中要注意的是獲取表單中第一個文本字段的代碼,因為表單在實例化后,items屬性指向的是MixedCollection實例,因為要在其items內才能找到文本自動對象。
接着完成的是onLogin方法,難度也不大, 就是先調用isValid方法,驗證表單是否符合提交要求,然后調用submit方法提交。其實不調用isValid也行,因為登錄按鈕只要在isValid為true時才能用。代碼如下:
onLogin: function () { var me = this, f = me.form.getForm(); if (f.isValid()) { f.submit({ //waitMsg: "正在登錄,請等待……", //waitTitle: "正在登錄", success: function (form, action) { window.location.reload(); }, failure: function () { }, scope: me }); } }
登錄成功(success配置項)后,會刷新一下頁面,讓頁面寫入驗證信息到Cookie。當然,也可以跳轉到另外一頁,不過筆者認為不如這樣來得簡便,這個稍后會說到。
登錄失敗(failure配置項),只寫了一個空函數的目的是因為表單的提交返回的數據格式是一樣的,處理方式也一樣,因而可使用同一個函數進行處理,但是還沒寫到,因而先保留一個空函數。
最后,別忘了將表單加入窗口的items里,這個必須放在調用callParent之前,不如不會初始化表單,代碼如下:
me.items = [me.form]
至此,登錄窗口就暫時寫好了。
以下是完整的Login.js代碼:
/// <reference path="../Ext.js" /> Ext.define("Ext.ux.Login", { extend: "Ext.window.Window", singleton: true, title: 'ExtJs Mvc登錄窗口', width: 450, height: 300, modal: true, closable: false, resizable: false, closeAction: 'hide', hideMode: 'offsets', initComponent: function () { var me = this; me.image = Ext.create(Ext.Img, { style: "cursor:pointer ", src: "/VerifyCode", listeners: { click: me.onRefrehImage, element: "el", scope: me } }); me.form = Ext.create(Ext.form.Panel, { border: false, bodyPadding: 5, bodyStyle: "background:#DFE9F6", url: "Account/Login", defaultType: "textfield", fieldDefaults: { labelWidth: 80, labelSeparator: ":", anchor: "0", allowBlank: false }, items: [ { fieldLabel: "用戶名", name: "UserName" }, { fieldLabel: "密碼", name: "Password", inputType: "password" }, { fieldLabel: "驗證碼", name: "Vcode", minLength: 6, maxLength: 6 }, { xtype: "container", height: 80, anchor: "-5", layout: "fit", items: [me.image] }, { xtype: "container", anchor: "-5", html: "**驗證碼不區分大小寫,如果看不清楚驗證碼,可單擊圖片刷新驗證碼。" } ], dockedItems: [{ xtype: 'toolbar', dock: 'bottom', ui: 'footer', layout: { pack: "center" }, items: [ { text: "登錄", width: 80, disabled: true, formBind: true, handler: me.onLogin, scope: me }, { text: "重置", width: 80, handler: me.onReset, scope: me } ] }] }); me.items = [me.form] me.callParent(arguments); }, onRefrehImage: function () { this.image.setSrc("/VerifyCode?_dc=" + (new Date()).getTime()); }, onReset: function () { var me = this; me.form.getForm().reset(); if (me.form.items.items[0]) { me.form.items.items[0].focus(true, 10); } me.onRefrehImage(); }, onLogin: function () { var me = this, f = me.form.getForm(); if (f.isValid()) { f.submit({ //waitMsg: "正在登錄,請等待……", //waitTitle: "正在登錄", success: function (form, action) { window.location.reload(); }, failure: function () { }, scope: me }); } } });