.Net Core應用框架Util介紹(四)

  上篇介紹了Util Angular Demo的目錄結構和運行機制,本文介紹Util封裝Angular的基本手法及背後的動機。

  Angular應用由TsHtml兩部分構成,本文介紹第一部分。

Angular Ts的封裝

  Ts是Angular的代碼部分,用於編寫頁面邏輯。

依賴注入( Dependency Injection )

  Ioc(Inversion of Control)已經成爲.Net Core服務端編程的標配,Ioc解耦了類和依賴類之間的創建關係,讓你開發出低耦合高內聚的系統。

  有了Ioc,你就可以面向抽象編程,注入依賴的接口,直接使用即可,而不用關心這個對象是如何創建出來的,也不用關心它具體是什麼類型。

  Angular革命性的把Ioc引入到客戶端腳本編程,從這裏也可以看出,Angular實際上更適合具有服務端編程經驗的開發人員。

  依賴注入是Ioc的一種使用模式,最常見的是構造方法依賴注入,將依賴對象定義在構造方法參數上,運行時,Ioc框架會把依賴對象“推送”進來,從使用的角度看,業務代碼並不知道Ioc的存在,業務代碼未與Ioc框架耦合,極易測試,代碼也極度清爽,這一度讓依賴注入成爲Ioc的代名詞,也是Ioc的推薦用法。

  當在Angular中使用依賴注入,有沒有什麼缺陷呢?下面來看個例子。

 

  這段代碼需要發送Http請求,所以從構造方法注入了Http。

  你必須要精確的知道Angular的哪個提供了Http服務,並且需要知道這個類在什麼路徑下。並不是每個類都像Http這麼簡短易記,這增加了大腦的負荷。

  對於專業Angular前端人員,這算不上什麼缺陷,不過它確實增加了小團隊使用Angular的學習成本。

  對於這個例子,哪怕你知道注入Http類,也清楚它的路徑,但可能仍然引入了未知的Bug。Angular還提供了一個叫HttpClient的類,它在@angular/common/http路徑下,HttpClient提供了攔截等高級機制,用來取代@angular/http包。如果你團隊的一部分人使用HttpClient,並對Http進行了攔截,比如設置全局Token,另一部分人使用Http類發送請求,就會導致潛在的BUG。

  現在你清楚了對Angular API進行封裝的必要性。

  封裝是一種隱藏實現細節的手段,封裝以後,你的注意力將從Angular Api轉移到自己的業務上。

  對於Http請求,理想的API可能長成這樣。 

 

  首先,你並沒有從@angular/common/http引入HttpClient,更沒有在構造方法上注入它。這解決了需要記憶API的問題。

  其次,你並不清楚util.http封裝的具體類型是什麼,這統一了團隊的API調用,也方便你在未來需要更換實現時不至於挨處查找。  

  我在前幾年使用EasyUi時也封裝了這樣的Helper,通過直接引用並簡單包裝就完成了任務。 

  Helper往往表現爲靜態方法,面對Angular的Ioc體系,封裝Helper變得很棘手,如何把需要的對象依賴注入到Helper靜態方法中呢? 

  你如果直接import引入Angular API,這樣封裝出來的Helper行爲上可能是錯的,比如HttpClient的攔截機制,import創建的HttpClient實例脫離了Ioc框架,這將導致攔截失效。 

  一種辦法是避開靜態方法,你可以注入Util類,這樣就可以拿到Angular Ioc容器中的相關實例了,這確實是個辦法。 

  不過你如果希望使用靜態方法,有沒有辦法現實呢? 

服務定位器( Service Locator )

  依賴注入威名遠播,以至於很多人並不清楚它還有個同父異母的弟弟 —— 服務定位器。

  服務定位器是Ioc的另一種使用模式,你可以在代碼中主動調用Ioc容器方法“拉取”依賴對象,這會導致你的代碼與Ioc框架耦合,讓代碼也更難測試

  服務定位器儼然成爲一種反模式,那麼它是否已經一無是處了呢?

  我發現在大多數情況下,並不需要服務定位器,但在某些情況下,它卻非常有用。

  哪種情況需要服務定位器?當你無法使用依賴注入時,就該它出手了。

  比如靜態方法,你就無法使用依賴注入,通過服務定位器方式仍然可以訪問Ioc,這對於封裝框架Helper有非常重大的意義,我會在本系列後續文章介紹服務定位器在.Net Core服務端封裝上的應用。 

  Util Angular Helper大量使用了服務定位器,以更簡單的方式提供常用API。 

Util Angular Helper介紹

  • Helper

  它位於/Typings/util/common/helper.ts,包含一些常用操作,比如空值判斷,類型轉換等,helper.ts內部將操作委託給lodash等第三方js庫。

  • IocHelper

 

  它位於/Typings/util/angular/ioc-helper.ts

  IocHelper內部保存了Angular Ioc容器實例,以方便其它Helper以服務定位器的方式來訪問Ioc容器。

  由於Angular Ioc具有分級特性,所以保存了模塊級組件級兩種容器,對於獲取路由參數等操作,必須從組件容器獲取實例,否則將導致錯誤的行爲。

  在什麼位置設置Angular Ioc容器呢?

  模塊級容器在AppModule根模塊中設置組件級容器需要在每個組件設置,這造成了不便,尚未找到更優雅的方式。


 

  下面演示了IocHelper的用法。

let client = util.ioc.get<HttpClient>(HttpClient);
  • HttpHelper

  對於業務操作,使用得最頻繁的Angular Api莫過於發送Http請求,從服務端獲取Json數據,或將表單數據傳遞給服務端處理。

  Util通過HttpHelperWebApiForm三個Helper從不同層次對Http操作進行了封裝

  HttpHelper位於/Typings/util/angular/http-helper.ts,對Angular HttpClient進行了簡單包裝,提供原始Http操作

  

  Util儘量提供同步Api,使用回調函數,而不是Rx的Observable或異步Promise,這樣團隊成員只要具備JQuery經驗就能開發,降低了團隊的學習成本。 

  在絕大多數情況下,你並不需要調用HttpHelper,WebApi和Form操作類會提供更多默認行爲。

  • WebApi 

  在發送Http請求時,你通常需要處理異常 

  異常可分爲Http異常業務異常兩類。 

  Http異常是未成功的Http響應,比如Http狀態碼爲500的服務器內部錯誤。Http異常通常和業務無關,所以每次發送請求設置Http異常處理是枯燥乏味的。 

  另一方面,Http返回200成功信號並不代表業務執行成功,所以不應該通過Http狀態碼來識別業務是否成功完成。 

  通過客戶端和服務端約定標準通信格式可以簡化異常處理。 

  先來看客戶端結果類型

 

 

  下面是服務端結果類型

 

 

  WebApi操作類位於/Typings/util/common/webapi.ts,它在HttpHelper的基礎上,設置了默認的Http異常處理,將Http異常輸出到瀏覽器控制檯,以方便排錯,另外將服務端返回結果轉換爲客戶端標準結果類型 

  下面展示了WebApi操作類的用法,handler方法是成功處理函數,你不用進行任何狀態判斷,WebApi內部已經處理過了。注意result參數並不是我們定義的標準Result類型,而是Result的data屬性,也就是實際業務類型,前後端標準通信格式被封裝起來。

  • Form 

  對於管理後臺,大多爲表單操作,所以我們對錶單需要特殊關照。 

  Form操作類位於/Typings/util/common/form.ts,它內置了一些表單常見操作。 

  當表單提交失敗,通常會提示一個錯誤消息,以指示用戶修正錯誤。

 

  錯誤提示是Form操作類的默認設置,你也可以取消它。

 

  有時候,你希望在提交表單前先確認一下。

 

 

  通過設置一個屬性就完成任務是不是很爽? 

 

  Form操作類還包含很多有用的功能,下面是它的參數定義

 1 /**
 2  * 表單提交參數
 3  */
 4 export interface IFormSubmitOption {
 5     /**
 6      * 服務端地址
 7      */
 8     url: string;
 9     /**
10      * 數據
11      */
12     data;
13     /**
14      * Http頭
15      */
16     header?: { name: string, value }[];
17     /**
18      * Http方法
19      */
20     httpMethod?: HttpMethod;
21     /**
22      * 確認消息,設置該項則提交前需要確認
23      */
24     confirm?: string;
25     /**
26      * 確認標題
27      */
28     confirmTitle?: string;
29     /**
30      * 表單
31      */
32     form?: NgForm;
33     /**
34      * 按鈕實例,在請求期間禁用該按鈕
35      */
36     button?,
37     /**
38      * 請求時顯示進度條,默認爲false
39      */
40     loading?: boolean,
41     /**
42      * 提交失敗是否顯示錯誤提示,默認爲true
43      */
44     showErrorMessage?: boolean;
45     /**
46      * 提交成功後是否顯示成功提示,默認爲true
47      */
48     showMessage?: boolean;
49     /**
50      * 提交成功後顯示的提示消息,默認爲"操作成功"
51      */
52     message?: string;
53     /**
54      * 提交成功後是否返回上一個視圖,默認爲false
55      */
56     back?: boolean;
57     /**
58      * 提交成功後關閉彈出層,當在彈出層中編輯時使用,默認爲false
59      */
60     closeDialog?: boolean;
61     /**
62      * 提交前處理函數,返回false則取消提交
63      * @param data 數據
64      */
65     beforeHandler?: (data) => boolean;
66     /**
67      * 提交成功處理函數
68      * @param result 結果
69      */
70     handler?: (result) => void;
71     /**
72      * 提交失敗處理函數
73      */
74     failHandler?: (result: FailResult) => void;
75     /**
76      * 操作完成處理函數,注意:該函數在任意情況下都會執行
77      */
78     completeHandler?: () => void;
79 }
Form操作類參數

  Form操作類建立在WebApi操作類之上,而WebApi操作類建立上HttpHelper之上,通過層層包裝,讓Http請求變得更加簡單易用。

  • RouterHelper 

  Angular提供了路由機制,路由訪問是僅次於Http請求的操作。

  通常需要從路由中獲取參數

  RouterHelper用於操作路由,位於/Typings/util/angular/router-helper.ts,在內部使用服務定位器訪問ActivatedRoute,簡化了路由訪問。


  • Message 

  表單操作經常需要彈出各類消息框,比如成功提示框,錯誤提示框,確認提示框等。

  Message操作類集成封裝了PrimeNg和Angular Material的消息框,它位於/Typings/util/common/message.ts

 

  下面彈出了一個錯誤消息框

util.message.error("哈哈");

  • Dialog

  Dialog操作類封裝了Material彈出層,位於/Typings/util/common/dialog.ts

 

 

  下面演示了將外部網頁加載到iframe中。

 

  加載業務組件應使用dialogComponent屬性。

 

Util Angular CRUD基類介紹 

  Js是一種弱類型語言,通過原型鏈和閉包可以模擬出面向對象的特徵,雖然看過一些文章說Js其實比C#這樣的面嚮對象語言更加OO,不過我始終沒有感覺出來,這或許是專業水平和我這種半吊子水平的區別所在吧。

  前幾年我對Js的封裝僅限於Helper或組件,服務端摸索出來的經驗很難應用到Js,雖然能模擬出我想達到的效果,但卻不是那麼直觀。

  雖然有人常說語言不是問題,語法更不是問題,但那指的是高手,在尚未達到高手境界以前,我們需要更優雅的語法糖,這使你寫起來心情舒暢,開發業務效率倍增。

  Typescript提供了強大的語法糖,包括面向對象基本語法,泛型,lambda表達式等,Angular則提供了Ioc等服務端才具備的特性,這對於具備服務端架構設計經驗的朋友,無疑是把利器。

  對於簡單Crud操作,來回就那幾句重複代碼,能否在Angular開發中像C#一樣封裝個基類呢?

  有了Typescript和Angular,這是非常輕鬆的任務。

  • TableQueryComponentBase 

  對於簡單Crud,通常在主界面放一個表格,並提供Crud操作。

  TableQueryComponentBase表格查詢基類,它提供了從表格刪除行,刷新表格,搜索等功能,位於/Typings/util/base/table-query-component-base.ts

  有了基類,業務組件將變得十分乾淨。

 

  Angular官方推薦將業務操作從組件分離,使用服務的形式依賴注入到組件,這讓你的設計更加內聚。

  不過我沒有機械的執行這一指南,僅在業務操作變得複雜時使用這種方式,在更多的簡單場景,我會把數據操作直接內置到組件中。

  • EditComponentBase

  EditComponentBaseCrud編輯基類,除了提交表單以外,它還能從路由取得Id並從服務端加載數據,位於/Typings/util/base/edit-component-base.ts

 

 

  ApplicationEditComponent示例類重寫了loadById和submit方法,刪除掉同樣可以工作,當你有特殊要求的時候進行重寫。

  • FormComponentBase 

  一個常見的需求,當表單已經被更改時,跳轉頁面需要提示用戶保存。

  FormComponentBase是表單基類,位於/Typings/util/base/form-component-base.ts,它提供了表單變更值檢查方法。

 

  • TreeTableQueryComponentBaseTreeEditComponentBase

  與TableQueryComponentBase和EditComponentBase類似,這兩兄弟也是用來支持簡單Crud操作的,不過它們用來支持樹型關係。

  繼承基類,收工,不要在簡單Crud上浪費過多時間。

 

 

  Util Demo的role示例演示了樹型Crud的用法。

小結

  本文簡單介紹了Util Angular Helper的封裝,使用服務定位器封裝成鏈式,所有helper都內聚在util這個命名空間下,這大幅提升了Angular的易用性,對於常用功能,不用記憶任何API,憑藉一點模糊的印象就能夠完成任務。

  另外介紹了爲簡化Crud提供的基類,這和服務端Crud封裝很相似,得益於Typescript和Angular所提供的強大語法糖。

  未完待續,Angular 組件封裝及TagHelper將在下篇介紹。

  寫文需要動力,請大家多多支持,點下推薦,Github點下星星

  Util應用框架交流一羣: 24791014

  Util應用框架地址:https://github.com/dotnetcore/util

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章