Ext JS提供了mvc和mvvm的應用程序框架支持,這兩種架構方法都是關注於將應用程序代碼和業務邏輯分離。每一種方法都有本身的優勢,這取決於怎麼分離應用程序模塊。下面咱們就簡單看一下:
什麼是MVC框架?
在一個MVC框架中,大多數的類要麼是模型(model)要麼是視圖(view)要麼是控制器(controller)。用戶(user)與視圖(view)交互,視圖(view)呢又顯示模型(model)中的數據(data)。這些交互都被控制器(controller)監控,控制器(controller)又在須要的時候經過更新模型(model)和視圖(view)與響應交互。
什麼是mvvm框架?
mvvm簡單說來就是在MVC的基礎上,新增了一個相似數據倉庫的概念。就是說我有了數據模型(model),也有了視圖展示(view),還有對其分發處理的控制器(controller)以後,多了一個儲蓄所,以下圖:
![](http://static.javashuo.com/static/loading.gif)
上面的三個模塊就是傳統的MVC模式,ExtJS在此基礎上提供了VM模塊。它發出請求,經過咱們指定的模型來組裝後返回,以後每次有數據請求時,均可以從這裏獲取數據,減小了服務器的請求次數。
層級結構關係
下面是ExtJS的MVVM框架的目錄結構,對於不知道Extjs環境如何部署的朋友,可參考這篇文章《
Extjs5.0集成Eclipse
》。
![](http://static.javashuo.com/static/loading.gif)
這裏可以很清楚的看到controller、model、store及view三個層次,也就是MVVM的分層目錄。這種思想在ExtJS4.X中也有體現,可是在5.X以後添加了新的特性,就是viewmodel和viewcontroller:
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
這裏的viewmodel就是MVVM中的VM,viewcontroller就是在原來controller基礎上的演進,目的是實現了動態加載,防止一次性加載過多的js致使的數據阻塞的問題。
![](http://static.javashuo.com/static/loading.gif)
上圖就展示了login文件夾下三個js文件的關係。Login.js是一個登錄窗體,調用的方法邏輯都在LoginController.js中,項目初始化的參數存儲在LoginModel.js,各司其職,很是明瞭。
下篇就以一個登陸的實例來介紹下ExtJS5.0中是如何實現的。
一、校驗
對於最基本的校驗固然是不能缺乏的。這裏的校驗包括對空值、輸入字段的長度及是否正確都進行了校驗,
好比以下兩圖:
二、驗證碼刷新
如上圖中驗證碼爲
R8DR,點擊後變爲
0PSS。
在後臺對驗證碼進行了大小寫處理,因爲大些更容易辨認,因此在前臺生成時用大些,輸入的驗證碼大小寫都是能夠的。
三、重置與肯定
點擊重置後會將輸入的內容清空
點擊肯定後則進入後臺頁面(目前仍是比較醜陋的),這裏利用session進行了限制,若是正常登錄後刷新頁面是不會從新登錄的。
下一篇螃蟹就登錄的ExtJS代碼進行詳解,核心代碼以下:
- Ext.define('app.view.login.Login', {
- extend: 'Ext.window.Window',
-
- requires: [
- 'app.view.login.LoginController',
- 'app.view.login.LoginModel',
- 'Ext.form.Panel',
- 'Ext.button.Button',
- 'Ext.form.field.Text',
- 'Ext.form.field.ComboBox'
- ],
-
- viewModel: 'login',
-
- controller: 'login',
- title: 'IT學習者-人事管理系統登錄',
- closable: false,
- width : 400,
- height : 230,
- cls: 'login',
- buttonAlign : 'center',
-
- items:[{
- xtype : "displayfield",
- value : "",
- height:30,
- margin : "0 0 0 0"
- },{
- layout : "column",
- items : [{
- columnWidth:.7,
- xtype: 'form',
- reference: 'form',
- defaults : {
- labelSeparator : ':',
- labelWidth : 60,
- width : 200,
- labelAlign : 'left'
- },
- defaultType : 'textfield',
- items : [ {
- xtype : 'textfield',
- fieldLabel : ' 用戶名 ',
- emptyText:"請輸入用戶名",
- regex : /([A-Za-z]{1})\w{1,19}/,
- regexText : '用戶名格式有誤',
- name : 'loginName',
- allowBlank : false,
- blankText : '用戶名不能爲空',
- minLength : 5,
- minLengthText : '用戶名的長度爲[5-16]',
- maxLength : 16,
- maxLengthText : '用戶名的長度爲[5-16]',
- margin : "10 10 10 50"
- }, {
- xtype : 'textfield',
- name : 'password',
- inputType : 'password',
- fieldLabel : '密 碼',
- fieldCls : 'password',
- emptyText:"請輸入密碼",
- inputType : 'password',
- allowBlank:false,
- blankText : '密碼不能爲空',
- minLength : 5,
- minLengthText : '密碼的長度爲[5-20]',
- maxLength : 20,
- maxLengthText : '密碼的長度爲[5-20]',
- margin : "15 10 10 50"
- },{
- xtype:'textfield',
- width:120,
- fieldLabel : '驗證碼',
- name : 'authcode',
- allowBlank:false,
- blankText : '驗證碼不能爲空',
- margin : "15 0 0 50"
- },{
- xtype:'panel',
- columnWidth:.4,
- height:30,
- html:"<a href='#' onclick='javascript:refreshCode();'><img id='validateCodeImg' title='點擊更換' alt='點擊更換' src='authCode' /></a>",
- margin : "-26 0 175"
- }]
- },{
-
- layout:'fit',
- bodyStyle: 'background:transparent',//設置爲透明,不不妨礙更換主題了
- columnWidth:.28,
- height:120,
- items:[{
- xtype : "displayfield",
- hideLabel : true,
- margin : "-105 0 0 0",
- value : "<img src='./images/itxxz.png' />"
- }]
- }]
- }],
- buttons: [{
- text: '肯定',
- listeners: {
- click: 'onLoginClick'
- }
- },{
- text: "重置",
- handler: function () {
- this.up('window').down('form').getForm().reset();
- }
- }]
- });
- //刷新驗證碼
- function refreshCode() {
- document.getElementById("validateCodeImg").src = "authCode?"+Math.random();
- }
的 javascript
剛開始學習的時候,螃蟹也是一頭霧水,可是因爲ExtJS5.0的書寫及其規範,API至關完整,因而,在學習的時候螃蟹儘可能的不脫離其設計思想,在原有的框架基礎上進行開發改進。
好比咱們定義一個類,就能夠經過如下的語法進行定義:
html
- Ext.define('TextClass', {
- name: 'itxxz',
- value: 'IT學習者'
- });
這樣,咱們就定義了一個TextClass的類,這個類裏有name和value兩個屬性,之後使用的時候,直接new或者Create就能夠了。
就比如上篇中咱們定義了登錄窗口,
java
- Ext.define('app.view.login.Login', {..}
這裏的定義就與TextClass的定義不一樣了,學過java的朋友可能更好理解一些,這裏的 app.view.login.Login 其實就是一個文件的路徑,而app並不是以下圖的根路徑,而是咱們定義的命名空間,具體的定義規則在《Extjs5.0集成Eclipse》中的sencha命令已有介紹。
那麼view.login.Login就是文件的路徑了,好比咱們的登錄窗口是定義的Login.js文件,那麼定義這個類的時候,規則就是命名空間加上路徑最後以類的名字結尾,這樣就能夠找到該文件了,就比如咱們定義的 app.view.login.Login同樣,以下圖:
![](http://static.javashuo.com/static/loading.gif)
這時候問題就來了,若是咱們想定義一個window窗口,該如何下手?
在ExtJS中也引入了集成的思想,這樣就避免了咱們重複發明輪子的煩惱,想實現什麼,只要extend一下就能夠了:
ajax
- extend: 'Ext.window.Window',
在咱們日常寫js的時候每每會有不少js的引入來相互配合,這在ExtJS中也有體現,就是經過require來實現:
數據庫
- requires: [
- 'app.view.login.LoginController',
- 'app.view.login.LoginModel',
- 'Ext.form.Panel',
- 'Ext.button.Button',
- 'Ext.form.field.Text',
- 'Ext.form.field.ComboBox'
- ],
這是咱們須要的js文件,經過本篇講解的類的定義規則,相信應該能夠看懂這種命名規範了。
下面咱們來看一下特殊的寫法:
json
- viewModel: 'login',
-
- controller: 'login',
這裏是引用viewModel和controller,有關這方面的介紹可看一下《ExtJS5.0的mvvm分層思想》。
經過上圖中能夠看到,login文件夾下一共定義了三個文件:Login.js、LoginController.js、LoginModel.js 。
Login.js的代碼在上一篇已經張貼了出來,下面先簡單看下另外的代碼:
LoginController.js
服務器
- Ext.define('app.view.login.LoginController', {
- extend: 'Ext.app.ViewController',
- alias: 'controller.login',
-
- loginText: 'Logging in...',
-
- constructor: function () {
- this.callParent(arguments);
-
- this.loginManager = new app.LoginManager({});
- },
-
- onSpecialKey: function(field, e) {
- if (e.getKey() === e.ENTER) {
- this.doLogin();
- }
- },
-
- onLoginClick: function() {
- this.doLogin();
- },
-
- doLogin: function() {
- var form = this.lookupReference('form');
- if (form.isValid()) {
- Ext.getBody().mask(this.loginText);
- this.loginManager.login({
- data: form.getValues(),
- scope: this,
- success: 'onLoginSuccess',
- failure: 'onLoginFailure'
- });
- }
- },
-
- onLoginFailure: function() {
- Ext.getBody().unmask();
- },
-
- onLoginSuccess: function() {
- Ext.getBody().unmask();
- this.fireViewEvent('login', this.getView(), null, null, this.loginManager);
- }
- });
LoginModel.js
cookie
- Ext.define('app.view.login.LoginModel', {
- extend: 'Ext.app.ViewModel',
- alias: 'viewmodel.login',
-
- // Just some data to seed the process. This might be pulled from a cookie or other
- // in a real app.
- data: {
- defaultOrg: 1,
- username: 'IT學習者-螃蟹'
- }
- });
在上面兩貼代碼的第三行,都有一個alias屬性,它的做用就是爲該類起一個別名,用過數據庫的朋友對此應該不陌生。
這樣再回頭看這兩行代碼應該知文識意了,沒錯,就是根據別名來引用的。
session
- viewModel: 'login',
-
- controller: 'login',
在這樣引用後,ExtJS會默認生成這兩個文件的對象了。
這篇就先介紹類的定義繼承及引用,下篇咱們繼續分析 架構
在前幾篇的教程中,螃蟹已經簡單介紹了Extjs如何加載java後臺的數據,不過是以項目中所定義的規範加載的,鑑於很多朋友依舊諮詢這方面的問題,這裏就以store爲例,單獨說明一下。
這是用於加載數據的請求,有些相似ajax,指定請求的url,請求的字段屬性,也就是fields中定義的屬性,再一個就是要指定根節點root。
- var store_itxxz = Ext.create('Ext.data.Store', {
- storeId:'simpsonsStore',
- fields:['empid', 'userName', 'sex'],
- proxy: {
- type: 'ajax',
- url: 'emp/queryAll',
- reader: {
- type: 'json',
- root: 'data'
- }
- },
- autoLoad: true
- });
而後就是如何調用的問題
- var grid = Ext.create('Ext.grid.Panel', {
- title: 'Simpsons',
- store: store_itxxz,
- columns: [
- {header: '登陸名', dataIndex: 'userName'},
- {header: 'empid', dataIndex: 'empid', flex:1}
- ],
- dockedItems: [{
- xtype: 'pagingtoolbar',
- store: store_itxxz, // GridPanel使用相同的數據源
- dock: 'bottom',
- displayInfo: true
- }]
- });
這裏第3行和第10行的store:store_itxxz中,左邊的store是指grid中定義的數據源屬性,右邊的store_itxxz是指咱們數據對象。
第10行是用來加載列表的數據,作顯示用,第10行爲分頁用。
java後臺代碼:
- /**
- * 員工列表
- * @return
- * @throws Exception
- */
- @RequestMapping("/queryAll")
- @ResponseBody
- public Map<String,List<Employee>> queryAllEmps() throws Exception{
- log.info("員工列表");
- List<Employee> allList = employeeService.selectAllEmps();
- Map<String,List<Employee>> map = new HashMap<String,List<Employee>>();
- map.put("data", allList);
- System.out.println(map);
- return map;
- }
後臺代碼中返回的是一個map,以ajax形式返回,是SpringMVC的基本用法,這裏就很少介紹了。
第12行,map中put了一個key爲data,value爲allList的元素,這裏的data就是store_itxxz中的root指定的根節點。
也能夠修改一下,好比根節點叫作itxxz_data,那麼再此處只須要將key改成itxxz_data,store_itxxz中獎root的定義data也改成itxxz_data就能夠了。
數據的返回格式以下:
{ data: [ {empid: '001', userName:'IT學習者', sex:'男'}, {empid: '002', userNmae:'螃蟹', sex:'女'} ] }
效果圖可參考《Extjs5.0 GridPanel數據動態加載》