Angularjs1.X進階筆記(1)—兩種不同的雙向數據綁定

来源:https://www.cnblogs.com/dashnowords/archive/2018/07/17/9325775.html
-Advertisement-
Play Games

一. html與Controller中的雙向數據綁定 html Controller的雙向數據綁定,在開發中非常常見,也是Angularjs1.x的宣傳點之一,使用中並沒有太多問題。 1.1數據從html流向controller 也就是從 視圖層 流向 模型層 ,原生html中需要使用表單元素(例如 ...


一. html與Controller中的雙向數據綁定

html-Controller的雙向數據綁定,在開發中非常常見,也是Angularjs1.x的宣傳點之一,使用中並沒有太多問題。

1.1數據從html流向controller

也就是從視圖層流向模型層,原生html中需要使用表單元素(例如input標簽)來收集用戶輸入信息,Angularjs中通過在表單元素上使用ng-model標簽,當用戶輸入信息時,同步將用戶輸入的信息賦值給controller中的變數:

<body ng-app="myApp">
   <div id="main" ng-controller="myCtrl">
       <p>改變輸出值:</p>
       <input type="text" ng-model="testInfo.content" ng-change="showInput()">
   </div>
 <script src="./angular.min.js"></script>
 <script>
    angular.module('myApp',[])
    .controller('myCtrl',['$scope',function($scope){
       $scope.showInput = function() {
          console.log($scope.testInfo.content);
        }
    }]);
 </script>
</body>

在頁面上輸入1234567即可看到,每次在頁面輸入數字後,控制台輸出的$scope,testInfo.content的值都和頁面保持一致:

1.2 數據從controller流向html

也就是從模型層流向數據層,當controller中的數據模型變數發生變化後,Angularjs又會根據數據模型的值去改變ng-model指令綁定的表單元素的值,使用ng-bind指令也可以被動獲得來自controller的數據流。

我們編寫如下demo進行測試:

<body ng-app="myApp">
    <div id="main" ng-controller="myCtrl">
        <button ng-click="add()">+1</button>
        <p>改變輸出值:</p>
        <input type="text" ng-model="testInfo.content">
        <p>使用ng-bind綁定的標簽:</p>
        <p ng-bind="testInfo.content"></p>
    </div>
    <script src="./angular.min.js"></script>
    <script>
    angular.module('myApp', [])
        .controller('myCtrl', ['$scope', function($scope) {
            //初始化
            $scope.testInfo = {
                content: 0
            }

            $scope.add = function () {
               $scope.testInfo.content += 1;
               console.log($scope.testInfo.content);
            }
        }]);
    </script>
</body>

demo中,每次點擊+1按鈕,$scope.testInfo.content的值會增加1,我們可以看到頁面上的結果:

1.3 你丫倒是刷視圖啊

來看看第一個活見鬼的例子,demo跟上面很類似,只是將滑鼠點擊觸發的方式改成了定時器自動觸發

<body ng-app="myApp">
    <div id="main" ng-controller="myCtrl">
        <button ng-click="add()">+1</button>
        <p>改變輸出值:</p>
        <input type="text" ng-model="testInfo.content">
        <p>使用ng-bind綁定的標簽:</p>
        <p ng-bind="testInfo.content"></p>
    </div>
    <script src="./angular.min.js"></script>
    <script>
    angular.module('myApp', [])
        .controller('myCtrl', ['$scope', function($scope) {
            //初始化
            $scope.testInfo = {
                content: 0
            }

            //定時自增
            setInterval(function () {
              $scope.testInfo.content += 1;
              console.log('$scope.testInfo.content的值現在是:',$scope.testInfo.content);
            },1000)
        }]);
    </script>
</body>

你會活見鬼地發現,數據模型一直在變,但是頁面卻沒有刷新:

這裡就是 Angularjs1.X雙向數據綁定中的第一個坑 ,你會發現$scope上綁定的數據模型html中顯示的內容有時候並不是實時關聯的。這其實和Angularjs1.X的執行機制有關係。

如果我們自己來考慮,javascript中有一個變數的值發生了變化,現在要將這個值同步到html頁面上,需要怎麼做呢?我們需要獲取到這個DOM元素,然後改變它的innerHTML屬性,如果是表單元素就修改value。其實Angularjs也是這樣做的,只不過使用了自己的封裝的方法——$apply()。那麼此處的問題其實就在於,在setInterval的回調函數中去修改數據模型的值時,沒有觸發$apply()方法來更新視圖,而通過調用Angularjs封裝的ng-*方法(例如ng-click點擊方法)來修改視圖模型時,會自動觸發$apply()方法,視圖也就同步刷新了。

  • 解決方案1

    使用Angularjs封裝過的$interval服務來實現定時任務,感興趣的讀者可以自己看一下Angularjs源碼中$intervalProvider的部分,就會發現在方法最後的地方調用了$rootScope.$apply()

  • 解決方案2

    如果依然使用javascript原生的定時方法,那麼則需要在修改完視圖的數據模型後,手動調用$scope.$apply()方法來將數據模型的變動同步到html頁面中。

二. Controller與Directive中的雙向數據綁定

除了controller與html中的雙向綁定,Angularjs中還有另一個雙向數據綁定,那就是controller與directive之間的綁定。綁定的形式有很多種,我們先來看一下最常見的雙向綁定

2.1 directive中的雙向數據綁定

在設定自定義指令的scope參數時,將屬性的值設置為=就可以實現雙向數據綁定,這裡API的解釋是:

父級controller中的指定變數會與自定義指令link函數中的變數相互影響

下麵的實例中,我們將看看controller中的數據模型$scope.testInfo.content的值與自定義指令中scope.pagination如何相互影響,是否如定義所說這裡的綁定真的是雙向的。示例界面如下(demo源碼請見附件demo.html文件):

  • 每次點擊+1按鈕,Scope.testInfo.content的值都會增加1
  • 每次點擊show $scope.testInfo,控制台都會列印出$scope.testInfo的值
  • 每次點擊標簽上的數字,則會列印出自定義指令中scope.pagination的值,並將該值進行自增

接下來的測試操作,我們將按照如下的流程進行:

  1. 點擊5次+1按鈕,再點擊5次數字標簽
  2. 點擊show $scope.testInfo按鈕

2.2 你丫怎麼又不刷新了

隨著上一節的操作步驟,我們一起來見證雙向數據綁定中又一次鬧鬼事件:

  • 點擊5次+1按鈕,再點擊5次數字標簽

    結果為:

我們看到,第一次點擊數字標簽時,控制台打出了link函數中scope.pagination的值為5,這說明$scope.testInfo.content的值被傳遞給了自定義指令中的scope.pagination,也就是說數據從controller流向了directive。而當我們再點擊4次數字標簽(一共點了5次)後,從控制台可以看出,scope.pagination的值已經成為10,而頁面上使用ng-bind指令獲取到的結果卻依舊是5。也就是說,數據從沒有從directive流向controller。是不是有一種被騙的感覺?彆著急,接著看。

  • 點擊show $scope.testInfo按鈕

    結果為:

當我們點擊show $scope.testInfo時,控制台列印出了$scope.testInfo.content的值為5,這下證據坐實了,明明說好的雙向數據綁定,然而當自定義指令中的scope.pagination改變時,$scope.testInfo.content並沒有跟著一起改變。But!!!!我們會發現,這個show $scope.testInfo點下去以後,頁面上通過ng-bind綁定的值卻變成了10。也就是說,數據又從directive流回了controller

官方建議使用$watch方法來追蹤scope中的變數,而當我們這樣做時,會發現$watch函數僅能追蹤到那些通過修改controller中的數據模型而影響link函數中變數的行為並更新視圖

這裡就是 Angularjs1.X雙向數據綁定中的第二個坑,controller和directive中所謂的雙向數據綁定,並不能追蹤指定變數的所有變化,而且不是同步完成的。

其實這裡的問題仍然和Angularjs的運行機制有關,解決方案如下:

  • 解決方案1

    使用自定義指令的templateUrl屬性替換當前指令的模板,使用ng-click指令來綁定一個點擊響應函數,在響應函數中改變scope.piganation的值。

  • 解決方案2

    在手動綁定的監聽回調中,修改自定義指令作用域內的變數後,使用scope.$emit( )方法通知其父級controller,併在controller中使用$scope.$on( )方法監聽同名事件,並修改對應的數據模型的值。

  • 解決方案3

    每當改變自定義指令中的變數值後,調用scope.$apply()方法,將directive中的變數值同步至controller的數據模型以及頁面。

三.原理和實戰總結

3.1 Angularjs中雙向數據綁定的基本原理

Angularjs中的雙向數據綁定,是通過一種叫做**"臟迴圈檢查(dirty-checking)"*的機制實現的。

其基本過程是這樣的,每當我們使用ng-modelng-bind指令將數據模型中的某個變數值和html頁面上某個標簽的內容聯繫起來時,Angular就會把這些變數放進一個WatchCollection的集合中,並自動幫我們來監控這些變數。每當WatchCollection中有變數出現變動時,Angular就會遍歷WatchCollection來查看是否有其他監控中的變數也被影響,每當有一個變數被影響,Angular都會在遍歷後再進行一次遍歷,直到某一次遍歷後WatchCollection中的變數都沒有變化,則Angular會認為當前的改動已經穩定了,然後才會將數據模型的變化同步到DOM元素上去,也就實現了數據綁定。

我們可以把WatchCollection理解為當前頁面的一種抽象,其中包含著頁面上所有有可能發生變化的部分。

3.2 雙向數據綁定的實踐經驗

想要在Angularjs項目中更加穩定地使用雙向數據綁定,筆者的建議是:

Angularjs項目中,儘可能地使用Angular告訴你的方式去編寫所希望實現的功能。

我們可以回顧一下上面在使用雙向數據綁定發生異常時的場景:

  • 使用了原生的定時器(Angular中你應該使用$interval,$timeout服務)
  • 用類原生方法(bind)為元素添加事件監聽器,併在回調函數中修改了變數的值(Angular中,你應該使用ng-click來實現點擊事件的監聽)
  • ...

你會發現,每當自己沒有按照Angular的方式去編寫代碼,或者沒有按照一個模塊設計的初衷去使用它時,就無法確切地得到期望的結果。這是很容易理解的,如果你沒有按照Angular要求的方式書寫代碼,憑什麼期望它對你的代碼做出100%正確的回應呢?至於上述兩種數據綁定中出現問題的解決方案,上文已經有所提及,此處不再贅述。

許多人都聽說過"儘量不要在controller中操作DOM"這句話,實際上它並不意味著你在controller中操作DOM會導致程式報錯,而是在說如果你同時使用jQueryAngular兩套系統來管理自己的代碼,但又沒有按照官方指定的方式來規避它們之間的衝突,那代碼很可能會變得不穩定。想想當年騰訊電腦管家360安全衛士將你的電腦卡死的場景,你就明白這樣做的結果了。

四. 小結——所謂高手

筆者曾經看過這樣一段話,覺得深有感觸:

所謂高手,是指那些熟知套路創意無窮的人。而高手之間的較量,歸根結底都是基本功的比拼。

願有朝一日,你也能成為高手。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 問題:scrollview內部組件都設置了android:layout_height="fill_parent"卻沒有效果。 解決辦法:設置scrollview的fillViewport屬性為"true"。 說明:需要設置scrollview的fillViewport屬性為"true"時,才能使其子 ...
  • 原文 http://git.devzeng.com/blog/simple-usage-of-realm-in-ios.html 主題 Realm iOS開發 Realm是由 Y Combinator 公司孵化的一款支持運行在手機、平板和可穿戴設備上的嵌入式資料庫(旨在取代CoreData和Sqli ...
  • 最近在協助調研 Apollo 生成的代碼是否有可能跨 Query 共用模型的問題,雖然初步結論是不能,並不是預期的結果,但是在調研過程中積累的一些經驗,有必要記錄下。如果你也對 Graphql 感興趣,不妨先從 Github 的 Graphql API 來切手實踐。 ...
  • 一,效果圖。 二,index.html代碼。 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Todo</title> <meta name="viewport" content="initial-scale=1, maximu ...
  • 今日看到一句話: 基於迴圈的迭代比基於函數的迭代法快8倍,因此有了該篇驗證博客。 驗證代碼如圖: 驗證結果:在數量比較少的時候,無明顯差別,當數量級達到10的4次方時候,for迴圈的效率優勢明顯。 從中學到其他小知識點: 1、es6語法 數組fill,填充數組,註意,如果填充的是對象,則只是一個指針 ...
  • 我們在工作中常常需要監聽某一個屬性值的變化,這個時候我們就需要用到了監聽屬性watch,在這裡我總結watch屬性的三種場景使用希望對你有所幫助: 1.基礎版監聽: 場景如下:輸入框輸入你的年齡,如果年齡在0 15歲提示信息:你還是個小孩,如果年齡在 15 25歲,提示信息:你已經是個少年,如果年齡 ...
  • 本人後端開發碼農一個,公司前端忙的一逼,項目使用的是easyui組件,其自帶的datebox組件使用起來非常不爽,主要表現在 1、自定義顯示格式很麻煩 2、選擇年份和月份用戶體驗也不好 網上有關於和My97DatePicker結合的例子,但感覺也用的不是很爽。 發現國內的layDate體驗非常滿意, ...
  • 1 2 3 4 5 Title 6 7 8 9 10 用戶名: 11 13 14 密&emsp;碼: 15 17 18 性&emsp;別: 19 ... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...