[AngularJS] ng-show及ng-if使用情境

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。

image

表單代碼大致如下

<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來隱藏/顯示小孩數量輸入框

image

執行後,當資料填寫完畢卻發現無法Submit資料,明明所有資料都填寫阿!!

image

查看一下Html元素後,發現用戶小孩數量輸入框也被一起驗證了。因為我們是使用ng-show來實作隱藏功能,因此該輸入框只是被CSS效果隱藏起來而已,事實上都還存在於DOM中,因此會納入表單驗證中。

image

所以在這個情境下我們應該使用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資料

image

image

勾選後顯示的輸入框也會納入驗證範圍,檢核所需之條件

image

image

 

結論


單就以表單驗證角度來說,如果有額外條件限定資訊需要顯示/隱藏時,可以使用ng-if透過DOM Tree元素的新增/移除來避免資料驗證上之困擾;反觀ng-show使用情境,我想可能是在資料欄位過多時,可利用此特性隱藏已填寫欄位,而該些隱藏的欄位也需納入表單驗證中做檢核。

 

補充說明


2017-06-30

筆者最近在處理一個首頁滑鼠移動到特定區塊後顯示 Menu 區塊破版問題,最後就是發現資料在載入中且滑鼠移動到該區塊,若使用 ng-if 判斷是否顯示該區塊時,會讓 Menu 區塊顯示時無法(來不及)顯示綁定的資料而造成版面怪怪,但如果調整為 ng-show 則不會有這個問題,提供此經驗供大家參考。

 


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !