angular-1.3 之ng-model-options指令


ng-model-options是angular-1.3新出的一個指令,這篇文章就來介紹這個指令的用法.

 

ng-model-options允許我們控制ng-model何時進行同步. 比如:1.當某個確定的事件被觸發的時候 2.在指定的防抖動延遲時間之后,這樣視圖值就會在指定的時間之后被同步到模型. 

為了了解它到底是什么意思,我們從一個最簡單的ng-model指令創建的input元素雙向數據綁定的栗子開始看起:

eg-0.0

核心代碼:

<input type="text" ng-model="name">
<p>Hello {{name}}!</p>

點擊查看效果: http://jsfiddle.net/gdjwk5u7/

這是我們都很熟悉的,在angular-1.2版本就可以實現的ng-model雙向綁定.

可以看到,它是實時同步更新的,input中每輸入一個字,它就立刻同步到數據模型,這是因為,每次輸入input,都會觸發一個input的事件,然后angular就會執行的$digest循環,直到模型穩定下來.我們不用手動設置任何事件監聽來同步更新視圖和模型,這樣很贊,這就是angular的目的.

然而,由於每次鍵盤按下,都會觸發$digest循環,所以當你在輸入input內容的時候,angular不得不處理所有綁定在scope上的watch監聽,所以,它執行的效率就取決於你在scope上綁定了多少watch監聽,以及這些監聽的回調函數是怎樣的,這個代價是十分昂貴的.

所以,如果我們能夠自己控制$digest的觸發,比如當用戶停止輸入300毫秒后觸發,又或者是當input元素失去焦點的時候再觸發,那不是更好么? 於是,angular-1.3的ng-model-options就為我們做了這件事.

 

一. 通過 updataOn 指定同步ng-model的時間

ng-model-options 提供了一系列的選項去控制ng-model的更新.

通過updateOn參數,我們可以定義input觸發$digest的事件.舉個栗子,我們希望當input失去焦點的時候更新模型,我們只需要按照如下的配置來實現:

eg-1.0

核心代碼:

<input 
      type="text" 
      ng-model="name" 
      ng-model-options="{ updateOn: 'blur' }"/>
<p>Hello {{name}}!</p>

在eg-0.0的基礎上,我們添加了 ng-model-options="{ updateOn: 'blur' }" 這樣一個指令.它告訴angular, 它應該在input觸發了onblur事件的時候再更新ng-model,而不是每次按下鍵盤就立即更新model. 點擊查看效果:(maybe需要牆~~~)

http://plnkr.co/edit/URMCoON9qDFnxdlyiDSS?p=preview 

如果我們想要保留默認的更新模型事件,另外再給它添加其它觸發$digest的事件,我們可以使用一個特殊的事件:default. 可以通過空格分隔的字符串來給它添加多個事件. 下面這段代碼能夠在輸入的時候同步更新模型,並且當input失去焦點的時候,也更新模型.

eg-1.1

核心代碼:

<input 
      type="text" 
      ng-model="name" 
      ng-model-options="{ updateOn: 'default blur' }"/>
<p>Hello {{name}}!</p>

點擊查看效果:(maybe需要牆~~~)

http://plnkr.co/edit/6VtaJrCIuO5ePfoz8UXA?p=preview 

(效果其實不太看不出來的...因為雖然blur的時候它在同步,但是其實輸入的時候已經同步完了)

好了,現在我們知道指定更新model的事件是怎么做的了. 接下來讓我們看看怎么指定更新的延遲時間.

 

二. 通過 debounce 延遲模型更新

我們可以通過ng-model-options來延遲模型的更新,以此來降低當用戶和模型交互時觸發的$digest循環的次數. 這不僅減少了$digest循環的次數,同時也是處理異步數據模型時提升用戶體驗度的一個好方法.

想象有這樣一個元素: input[type="search"] ,每當用戶正在輸入的時候,數據模型就會更新,並且用最新的字段向后台提交請求.這樣是沒錯的.然而,我們很可能並不想讓用戶每次按鍵的時候就立刻更新模型,而是希望當用戶輸入完了一段有意義的搜索字段以后才更新模型. 在這種情況下,我們正適合使用ng-model-options的 debounce參數. debounce定義了模型更新的延遲毫秒數(需要是整數). 比如剛才提到的這種情況,我們希望當用戶停止輸入1000毫秒以后再更新模型,(停止輸入1000毫秒差不多應該就是輸入完了一段有意義的內容了吧).我們可以像下面這樣,定義debounce參數的值為1000:

eg-2.0:

核心代碼:

<input type="search" ng-model="searchQuery" ng-model-options="{debounce:1000}">
<p>Search results for: {{searchQuery}}</p>

現在,當輸入搜索內容的時候,會有1秒的延遲. 點擊查看效果:(maybe需要牆~~~)

http://plnkr.co/edit/lpFwWsTvZxMGfHFe38Bk?p=preview 

我們還可以做更多的配置: 為指定的事件指定延遲時間. 為不同的事件指定不同的延時,可以通過給debounce屬性定義一個json對象來實現: 屬性名代表事件名,屬性值代表延遲時間.如果某個事件不需要延遲,那么它的屬性值就是0.

下面這個栗子實現了這樣的模型: 當用戶在input里輸入的時候,延遲1000毫秒更新模型,但是當input元素失去焦點的時候,立刻更新模型:

eg-2.1: 

核心代碼:

<input type="search" ng-model="searchQuery" ng-model-options="{updateOn:'default blur',debounce:{default:1000,blur:0}}">
<p>Search results for: {{searchQuery}}</p>

點擊查看效果:(maybe需要牆~~~)

http://plnkr.co/edit/yYsfcjW8KVt9ZESQUSB2?p=preview 

 

三. 通過 $rollbackViewValue方法 同步模型和視圖

由於我們通過ng-model-options來控制了模型的更新時間,所有,在很多時候,模型和視圖就會出現不同步的情況. 舉個栗子,我們配置ng-model-options,讓input在失去焦點的時候同步數據模型,當用戶正在輸入內容時,數據模型沒有發生更新,所以input的value指向的是模型里真實的值(但是視圖上看到的是用戶輸入的值)

假設在這種情境下,你希望在數據模型更新前把視圖上的值回滾到它真實的值.這時,angular提供了一個叫做$rollbackViewValue的方法來為我們同步數據模型到視圖. 這個方法會把數據模型的值返回給視圖,同時取消所有的將要發生的延遲同步更新事件.

為了解釋這個方法,我寫了兩個demo來感受一下:

eg-3.0

核心代碼:

<div class="container" ng-controller="Rollback">
  <form role="form" name="myForm2" ng-model-options="{ updateOn: 'blur' }">
    <div class="form-group">
      <label>執行了 $rollbackViewValue() 方法</label>
      <input name="myInput1" ng-model="myValue1" class="form-control" ng-keydown="resetWithRollback($event)">
      <blockquote>
        <footer>myValue1: "{{ myValue1 }}"</footer>
      </blockquote>
      <p></p>
    </div>
    <div class="form-group">
      <label>沒有執行了 $rollbackViewValue() 方法</label>
      <input name="myInput2" ng-model="myValue2" class="form-control" ng-keydown="resetWithoutRollback($event)">
      <blockquote>
        <footer>myValue2: "{{ myValue2 }}"</footer>
      </blockquote>
    </div>
  </form>
</div>
app.controller('Rollback',function($scope){
    $scope.resetWithRollback = function(e){
        if(e.keyCode == 27) {
            $scope.myForm2.myInput1.$rollbackViewValue();
        }
    };
    $scope.resetWithoutRollback = function(e){
        if(e.keyCode == 27){
            angular.noop()
        }
    }
});

點擊查看效果:(maybe需要牆~~~) http://plnkr.co/edit/iMY8IqH5f8NLuIAxY8zN?p=preview 

按照下圖所示的順序操作:

myValue1使用了$rollbackViewValue()方法,可以回滾文本域里的值和數據模型同步,但是myValue2是不能的.

看一遍這個demo,也就知道了$rollbackViewValue()方法的意思和作用了.

 

*需要特別注意的一點是,在使用了ng-model-options這種情況下,如果直接修改模型值,有時可能讓視圖同步,有時卻不能,什么意思,看這個栗子:

eg-3.1:

html部分同eg-3.0.

app.controller('Rollback',function($scope){
    $scope.resetWithRollback = function(e){
        if(e.keyCode == 27) {
            $scope.myValue1 = ''; //使用了$rollbackViewValue,總是可以同步視圖,清空myValue1值
            $scope.myForm2.myInput1.$rollbackViewValue();
        }
    };
    $scope.resetWithoutRollback = function(e){
        if(e.keyCode == 27){
            //並不是每次都可以成功的同步的,有時可以,有時不可以.
            $scope.myValue2 = ''
        }
    }
});

點擊查看運行效果:(maybe需要牆~~~) http://plnkr.co/edit/vve2Xh7LROQLQFa6FFrn?p=preview

在eg-3.0的基礎上,我們做了一個小修改,就是按Esc的時候,不是直接回滾視圖值到當前的數據模型,而是先設置數據模型為空,然后再回滾視圖值.而myValue2,直接設置數據模型為空,不使用回滾.

在demo里多試幾次就會發現,在這種情況下,在myValue2的input里按Esc,有時可以同步視圖值為空,有時則不能.

所以,在用了ng-model-opitons的時候,如果在模型沒有被視圖同步之前需要讓視圖被模型同步,不能簡單通過設置模型,必須使用$rollbackViewValue()方法.

 

翻譯參考:EXPLORING ANGULAR 1.3 - NG-MODEL-OPTIONS 以及 https://docs.angularjs.org/api/ng/directive/ngModelOptions (要牆哦~~~)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM