如何優雅的使用 Angular 表單驗證

why520crazy發表於2019-01-15

隨便說說,這一節可以跳過

去年參加 ngChine 2018 杭州開發者大會的時候記得有人問我: Worktile 是什麼時候開始使用 Angular 的,我說是今年(2018年) 3 月份開始在新模組中使用最新的 Angular 6,他說是不是有點晚了,我當時愣了一下。 其實仔細回想了一下, Angular 2.0 正式釋出也就在 2016-09-14 號,所以也就晚了 1 年多一點點才開始使用而已嘛,再加上 2.0 到 4.0 的過渡或多或少還是有一點點坑的,不是很穩定,此時投入不是特別合適,雖然升級簡單,但是還是有很多概念和API需要重新學習,比如 HttpClient,路由等等(在 2.0 的時候我們是有嘗試在邊緣的一個 Open API Doc 文件站點使用過的,我們一直在關注著最新的 Angular)

所以我覺得 4.0 (也就是 2017-03)之後的 Angular 才真真正正的開始趨於穩定,可以開始嘗試學習和使用,再加上經過了 1 年多的社群實踐,踩坑,基礎生態建設,我覺得今年(2018年)才是企業大規模嘗試使用 Angular 最佳時間點,再加上 Angular CLI 以及 @angular/cdk的逐漸強大, 我想說這是一個最好的時代。

另外還有一個原因是 2016 - 2017 年屬於我司最艱難的困難時期,壓根沒有心思考慮切換最新技術棧,所以那 2 年基本上屬於埋頭做業務。

現在回過頭來看 Angular ,在前端框架高速發展的那幾年,因為正式版釋出遲遲延期,導致市場被後起之秀 React, Vue 等優秀框架佔有,好在新的 Angular 足夠優秀,足夠前瞻,值得花更多的時間投入學習和使用,現在還不算晚。所以慢有一定道理的,因為需要精雕玉琢,衡量未來的趨勢等等,但是從完美支援 TypeScript,RxJS 這 2 點來說,Angular 的確走在了前面。

迴歸正題,說到表單,我認為一個強大表單應該包含下面3部分功能

  1. 收集使用者的輸入的表單資料,在 Angular 中通過 ngModel 實現雙向繫結非常方便;
  2. 通過各種驗證器驗證表單元素輸入的資料是否合法,Angular 內建了常用的驗證器(required、pattern、email,min,max,minLength,maxLength);
  3. 驗證後給予使用者反饋,比如驗證不通過給予錯誤的提示資訊。

我覺得 Angular 的表單無疑是三大框架中最強大的,沒有之一,而且是官方原生提供支援和維護,提到 Angular 的表單肯定要說下 Angular.js 的表單,其實 Angular 的表單基本上繼承了 Angular.js 表單的所有功能,同時比 Angular.js 更強大,API 更友好。

另外說下本文不是普及 Angular Form 表單的基本知識的,如果有不瞭解的可以看 angular forms guide ,因為官方文件已經寫的特別好了。

那麼 Angular 的表單和 Angular.js 相比到底有哪些改進呢?

自定義 ngModel

在 Angular.js 中 ng-model 只能用於 input,select 等內建的 HTML 表單元素,如果是一個自定義的 select 框(div),可能就無法使用自帶的 required 等驗證器了

但是可以通過猥瑣的方式處理,比如加一個隱藏的原生 HTML 表單元素,這個元素上繫結的 ng-model 和自定義的 select 框的 model 是一樣的,然後通過這個隱藏元素是否驗證通過去控制自定義 Select 的驗證樣式

那麼在 Angular 中可以很方便讓任何自定義的元件使用 ngModel 和 內建的驗證器,只要你的自定義元件實現 ControlValueAccessor 介面,同時在元件的 providers 中加上 NG_VALUE_ACCESSOR 的 provider 即可,具體如何實現一個自定義的支援 ngModel 元件自行搜尋下,官方文件好像沒有找到相關介紹, 附一個 stackoverflow question

結構型指令內部的表單元素自動識別

在 Angular.js 中如果有 ng-if 之類的動態指令,內部的表單元素不會自動追加到 Form 上,必須通過擴充套件一個自定義指令 dynamicFormControl 去手動追加到 ngForm 上,但是在 Angular 中不需要使用者自己去處理,只要元素被渲染,會自動附加到 ngForm Controls 中。

響應式表單

Angular 中除了模板驅動表單外,還新增了響應式表單,讓使用者多了一份選擇,在某些複雜的場景下,響應式表單會更有優勢。

動態表單支援更好

在 Angular 中不管是模板驅動表單還是響應式表單,對於動態建立表單的支援都很好,可以輕鬆的通過 [attr.name]="formName"[name]="formName" 實現動態表單元素的建立。如果有複雜的驗證器,那麼使用 響應式表單 會更好。

模板驅動表單驗證器支援屬性繫結,動態控制是否需要驗證

如果一個表單元素(比如說使用者名稱)是否為必填不是確定的,而是動態設定的, 在 Angular 中可以通過屬性繫結 [required]="isRequired" 非常方便的控制,我看了下 Angular.js 的原始碼現在也是支援的,不知道是我以前沒有發現呢還是之後的版本加上的功能。

支援非同步驗證器

如果要驗證使用者名稱輸入是否已經存在,就需要請求 API 遠端驗證,那麼這個驗證就是一個非同步,如果驗證不支援就會導致驗證結果沒有返回的時候就直接提交表單了。如果支援非同步會更加的完善。

通過上述的幾點來看, Angular 表單基本已經完美了,但是

我們還需要讓驗證錯誤提示更加簡單

回頭再看下開頭的介紹的表單應該包含下面3部分功能

  1. 收集使用者的輸入的表單資料; 這個基本上 ngModel 雙向繫結的語法糖已經簡化的不能再簡化了,當然使用響應式表單連 ngModel 也可以不寫;
  2. 內建的驗證器滿足大部分場景,但是還是會有很多常用的驗證器官方沒有提供,比如 重複驗證,遠端唯一性驗證等等,@Nightapes/ngx-validators@gangachris/ng-validators 這2個第三方庫擴充套件了很多,即使不滿足自己擴充套件也很簡單;
  3. 驗證後給予使用者反饋,驗證不通過給予錯誤的提示資訊。對於這個錯誤提示資訊,每個產品每個使用者都會有不一樣的需求,Angular 把可以做到的都做到了,都自動追加了 ng-invalidng-validng-touched 等 class,還有就是哪些元素哪個驗證器驗證失敗都可以從 ngModel 和 ngForm 方便的獲取到,錯誤提示只能交給使用者自己去處理。

對於驗證錯誤提示,手動寫錯誤提示的模版會很囉嗦,寫模版本身也沒什麼,怕就怕哪天設計師改需求了,原有的提示方式換了一個新的方式,那整個系統都需要挨個替換,有追求的程式設計師最怕的就是做重複沒有含量的工作,而且有時候還無法通過批量替換完成,所以在使用 Angular.js 1.x 的時候我就封裝了一個表單驗證庫 angular-w5c-validator,剛開始釋出的時候功能比較簡單,後來有人提各種 Issue,逐漸改善,我覺得這個驗證庫對於很多人來說還是有幫助的,至少我覺得是更優雅的處理了各種錯誤提示,star 不多,但是證明了這個封裝還是有一定價值的。

那麼我們即使現在升級到了 Angular ,也面臨著錯誤提示如何處理的問題,當然也有些類庫處理了相關問題,但是好像都沒有找到特別好用的。

  1. ngx-errors 還是手寫模版,只是簡化了寫法。

  2. ng-zorro-antd 元件庫關於表單元件對錯誤驗證提示也做了很多工作,但是還是需要手寫模版配置。

既然沒有相關的類庫符合我們的需求,那麼顯然就需要自己造輪子,所以我們去年在升級 Angular 時就按照我們的方式在元件庫的 Form 表單模組加上了和 Angular.js angular-w5c-validator 類似的 API,得益於 Angular 框架的優秀,造起輪子特別簡單。

內部的元件庫暫時還無法開源出去讓更多人使用,但是的但是

ngx-validator 已經可以開始使用了

所以這周我單獨抽離了表單驗證功能為一個獨立的元件 ngx-validator , 如果你也再為表單驗證錯誤提示苦惱,也在尋找一種更優雅的錯誤處理方式,希望我的這個庫可以幫助到你或者給你一個啟發。

ngx-validator Demo 示例,點選直接檢視演示

ngx-validator.gif

github.com/why520crazy…

最後的最後感謝你耐心閱讀到此,這篇部落格已經計劃了 3 個多月了,因為工作繁忙一直沒有時間,這週末下了一個狠心,必須完成!已經被兒子打擾多次,還有就是 ngx-validator 目前基本的功能已經完成,後期還有很多增強的驗證器,測試需要補充,還不是特別完善,歡迎大家提寶貴意見! 開源真不容易,這麼簡單的一個功能花了我週末2天時間寫示例,文件,反覆修改 API,然後用蹩腳的英文寫了 Readme。

文章來源於 Worktile 部落格 解讀,想了解更多關於企業協作內容可前往檢視。

歡迎大家關注知乎專欄 ng-nice

相關文章