ng-show及ng-if使用情境
前言
在某些情況下需要對畫面上特定區塊進行顯示/隱藏的切換,此時就可以透過ng-show及ng-if來達成所需功能。如果單從畫面效果來看,其實兩者並無任何差別,但其背後運作原理卻是大不相同。其中ng-show是透過CSS(display: none !important)設定來隱藏頁面區塊,而ng-if是直接移除DOM Tree元素來隱藏頁面區塊。以下將就驗證情境來比較兩者差異。
情境實作
假設今天有個新增User需求,需要取得用戶小孩數量,但不一定每個用戶都已結婚生子,因此設計個checkbox來讓用戶勾選;當用戶勾選Has Children後,顯示輸入框且必須檢核是否填寫有效數字。最後Submit按鍵會依照表單驗證結果來決定是否Disable/Enable。
表單代碼大致如下
<form name="userForm" ng-controller="UserController as ctrl" ng-submit="ctrl.createUser(userForm.$valid)" novalidate>
<table class="table-condensed">
<tr>
<td>UserName</td>
<td>
<!-- UserName -->
<input type="text"
ng-model="ctrl.user.UserName"
name="UserName"
class="form-control input-sm" required />
<!-- 驗證UserName之錯誤訊息 -->
<div ng-messages="userForm.UserName.$error"
ng-show="userForm.UserName.$dirty"
ng-messages-include="commonMessage">
</div>
</td>
</tr>
<tr>
<td>Address</td>
<td>
<!-- Address -->
<input type="text"
ng-model="ctrl.user.Address"
name="Address"
class="form-control input-sm" required />
<!-- 驗證Address之錯誤訊息 -->
<div ng-messages="userForm.Address.$error"
ng-show="userForm.Address.$dirty"
ng-messages-include="commonMessage">
</div>
</td>
</tr>
<tr>
<td>
<!-- 是否隱藏ChildrenNumber輸入框之條件 -->
<input type="checkbox"
ng-model="ctrl.user.HasChildren"
htmlattributes="{ class = form-control input-sm }"
id="HasChildren">
<label class="control-label" for="HasChildren">Has Children</label>
</td>
<!-- ** 此處使用ng-show來隱藏ChildrenNumber輸入框 -->
<td ng-show="ctrl.user.HasChildren">
<!-- ChildrenNumber -->
<input type="number"
ng-model="ctrl.user.ChildrenNumber"
name="ChildrenNumber"
class="form-control input-sm" required />
<!-- 驗證ChildrenNumber之錯誤訊息 -->
<div ng-messages="userForm.ChildrenNumber.$error"
ng-show="userForm.ChildrenNumber.$dirty"
ng-messages-include="commonMessage">
</div>
</td>
</tr>
</table>
<!-- 表單驗證通過才Enable此按鍵 -->
<button type="submit" class="btn btn-info" ng-disabled="userForm.$invalid">Submit</button>
</form>
<!-- 建立AngularJS Module & Controller -->
<script>
// app module
angular
.module('app', ['ngMessages']);
// user controller
angular
.module('app')
.controller("UserController", function () {
var vm = this;
vm.createUser = function (isValid) {
if (!isValid) return;
// post vm.user data to web api
};
});
</script>
<!-- 定義共用的錯誤訊息(可抽出為獨立檔案) -->
<script type="text/ng-template" id="commonMessage">
<p ng-message="required" class="text-danger field-validation-error">此欄位必填</p>
<p ng-message="minlength" class="text-danger field-validation-error">輸入太少</p>
<p ng-message="maxlength" class="text-danger field-validation-error">輸入太多</p>
<p ng-message="number" class="text-danger field-validation-error">只能輸入數字</p>
<p ng-message="pattern" class="text-danger field-validation-error">格式錯誤</p>
<p ng-message="date" class="text-danger field-validation-error">只能輸入日期</p>
</script>
特別注意,此處先使用ng-show來隱藏/顯示小孩數量輸入框
執行後,當資料填寫完畢卻發現無法Submit資料,明明所有資料都填寫阿!!
查看一下Html元素後,發現用戶小孩數量輸入框也被一起驗證了。因為我們是使用ng-show來實作隱藏功能,因此該輸入框只是被CSS效果隱藏起來而已,事實上都還存在於DOM中,因此會納入表單驗證中。
所以在這個情境下我們應該使用ng-if來實作此效果,調整一下代碼如下
<tr>
<td>
<!-- 是否隱藏ChildrenNumber輸入框之條件 -->
<input type="checkbox"
ng-model="ctrl.user.HasChildren"
htmlattributes="{ class = form-control input-sm }"
id="HasChildren">
<label class="control-label" for="HasChildren">Has Children</label>
</td>
<!-- ** 此處使用ng-if來隱藏ChildrenNumber輸入框 -->
<td ng-if="ctrl.user.HasChildren">
<!-- ChildrenNumber -->
<input type="number"
ng-model="ctrl.user.ChildrenNumber"
name="ChildrenNumber"
class="form-control input-sm" required />
<!-- 驗證ChildrenNumber之錯誤訊息 -->
<div ng-messages="userForm.ChildrenNumber.$error"
ng-show="userForm.ChildrenNumber.$dirty"
ng-messages-include="commonMessage">
</div>
</td>
</tr>
當資料填寫完畢後即可正常Submit資料
勾選後顯示的輸入框也會納入驗證範圍,檢核所需之條件
結論
單就以表單驗證角度來說,如果有額外條件限定資訊需要顯示/隱藏時,可以使用ng-if透過DOM Tree元素的新增/移除來避免資料驗證上之困擾;反觀ng-show使用情境,我想可能是在資料欄位過多時,可利用此特性隱藏已填寫欄位,而該些隱藏的欄位也需納入表單驗證中做檢核。
補充說明
2017-06-30
筆者最近在處理一個首頁滑鼠移動到特定區塊後顯示 Menu 區塊破版問題,最後就是發現資料在載入中且滑鼠移動到該區塊,若使用 ng-if 判斷是否顯示該區塊時,會讓 Menu 區塊顯示時無法(來不及)顯示綁定的資料而造成版面怪怪,但如果調整為 ng-show 則不會有這個問題,提供此經驗供大家參考。
希望此篇文章可以幫助到需要的人
若內容有誤或有其他建議請不吝留言給筆者喔 !
