Tìm hiểu về apply() và digest() trong AngularJS

Giới thiệu

$apply() và $digest() là 2 method cơ bản trong AngularJS.Chúng khá là khó hiểu và dễ gây nhầm lần khi sử dụng. Bài viết này sẽ giải thích rõ hơn về $apply() and $digest() và cách chúng hoạt động trong các ựng dụng AngularJS.

$apply() and $digest()

AngularJS sử dụng chức năng two way data binding để thực hiện việc trao đổi dữ liệu giữa view và model. Data binding là cách mà khi bạn thay đổi 1 vài thứ trên view thì scope model sẽ tự động thay đổi theo. Tương tự như vậy,thì khi nào scope model thay đổi thì view sẽ thay đổi với các value mới đó. AngularJS đã làm cách đó như thế nào vậy? Đó là khi chúng ta sử dụng expression ({{aModel}}), Angular sẽ khởi tạo 1 watcher trên scope model, để lắng nghe toàn bộ thay đổi của view và scope model đó.Ví dụ :

$scope.$watch('aModel', function(newValue, oldValue) {
  //update the DOM with newValue
});

Tham số được truyền vào trong function $watch() đuợc biết đến như là listener function, và sẽ được gọi đến bất cứ khi nào mà aModel có sự thay đổi. Và khi các giá trị của aModel thay đổi thì AngluarJS sẽ thực hiện việc updated dữ liệu ở trên HTML. Có 1 câu hỏi ở đây ,đó là làm thế nào mà Angular có thể phát hiện ra khi nào thì sẽ gọi các listener function? hay nói cách khác là làm thế nào AngularJS biết khi nào thì aModel có sự thay đổi để thực hiện các corresponding listener? Liệu nó chạy một chức năng định kỳ để kiểm tra xem giá trị của model đã thay đổi? Trả lời ,đó là công việc của các digest() cycle đang thực hiện.

Vậy công việc của các digest() là như thế nào ?

Khi bạn thay đổi 1 scope model thông qua 1 sự kiện ng-click của 1 directive thì AngularJS tự động gọi $digest cycle thông qua method $digest().Khi các digest() cycle bắt đầu chạy nó sẽ bắt đầu tìm kiếm các watchers, khi phát hiện các thay đổi của từng watcher ,nó sẽ thực thi các function tương ứng cho các watcher đó,Kết quả của công việc này là bạn thấy được sự thay đổi trên view của các scope model.

<div ng-controller="myController">
    {{data.time}}

    <br/>
    <button ng-click="updateTime()">update time - ng-click</button>
    <button id="updateTimeButton"  >update time</button>
</div>

<script>
    var module       = angular.module("myapp", []);
    var myController1 = module.controller("myController", function($scope) {

        $scope.data = { time : new Date() };

        $scope.updateTime = function() {
            $scope.data.time = new Date();
            $scope.$apply(); //this triggers a $digest
        }

        document.getElementById("updateTimeButton")
                .addEventListener('click', function() {
            console.log("update time clicked");
            $scope.data.time = new Date();
            $scope.$digest();
        });
    });
</script>

1 vài lưu ý khi sử dụng digest() và apply()

Về performence thì $scope.digest() sẽ có tốc độ nhanh hơn vì nó chỉ lắng nghe các watchers của chính bản thân $scope và các scope con của nó ,còn $scope.apply() thì lại lắng nghe và gọi lại lớp cha là $rootScope.digest(), như vậy phạm vi nó sẽ lớn hơn rất nhiều .

$digest cycle thông thường sẽ ko chạy duy nhất 1 lần.Tại thời điểm kết thúc của vòng lặp hiện tại, nó sẽ thực hiện lại việc chạy lại để kiểm tra toàn bộ xem có 1 model nào có sự thay đổi . Đây là cơ chế mà AngularJS gọi là dirty checking. chu kỳ $digest() sẽ lặp cho đến khi không có sự thay đổi của model nào, hoặc nó chạm vào số lần lặp tối đa là 10.