Angular Controller and Scope

Mở đầu

  • Có khá nhiều bài viết giới thiệu chuyên sâu về AngularJS và scopes.
  • Bài viết này chỉ đơn giản giới thiệu angular controller và Scopes trong phạm vi tìm hiểu của bản thân.

Đôi điều cơ bản về Controller

  • Controller được xem như là cầu nối giữa Model và View. Nếu bạn tìm hiểu sơ qua về AngularJs, hoặc có những khái niệm cơ bản về mô hình MVC, bạn sẽ hiểu tầm quan trọng không thể thiếu của Controller trong mô hình MVC và sử dụng nó. Về mặt cơ bản thì Controller cung cấp dữ liệu và các dịch vụ để view sử dụng,và định nghĩa các xử lý logic, đồng thời cũng là nơi chuyển tiếp các hành động của người dùng để lưu trữ dữ liệu một cách đúng đắn và hợp lý. Bạn có thể hình dung đơn giản Controller giống như một cây cầu nối giữa hai hòn đảo độc lập Model và Views.

  • Nói vậy cũng chưa thực sự chính xác, nhưng có những cái nhìn khái quát và những khái niệm cơ bản về AngularJS bạn có thể tham khảo Tai Đây. Bài viết này chỉ tập trung vào Angular controller và scopes.

  • Về mặt mô hình, thì AngularJs có mô hình MVW(model-view-whatever), có nghĩa là hai thành phần không thể thiếu là model và view, phần còn lại là gì cũng được. Do đó, AngularJs có thể áp dụng thành mô hình MVC(model-view-controller) hoặc nhiều mô hình với model-view là phần cốt lõi, trụ cột.

  • Về mặt lý thuyết, thì model sẽ trực tiếp tương tác với view, và thay đổi từ view cũng có thể dẫn đến thay đổi trực tiếp tại model, không nhất thiết phải có Controller, và controller không phải là thành phần không thể thiếu như trong mô hình MVc nữa. Ở đây ta có thể thấy sự linh hoạt của AngularJS, có thể phù hợp với nhiều mô hình khác nhau, nhưng điều đó không có nghĩa phủ nhận sự quan trọng của Controller trong thực tế phát triển những ứng dụng Web.

  • Cú pháp khai báo một Controller trong angualarJs là :

        app.controller("sampleController", ["$<các thành phần injection>", function(<các thành phần sử dụng>){
        //dinh nghia cac controller o day
        };
  • Khi định nghĩa các controller như ở trên, bản chất là ta định nghĩa các thành phần, cấu trúc, cơ chế để tạo ra một đối tượng kiểu sampleController, thực tế, mỗi khi sử dụng ng-controller ta đều clone ra một thể hiện của lớp samplecontroller đã định nghĩa.

  • Dễ hiểu hơn, ta có thể hiểu rằng, khi định nghĩa một controller, bản chất là ta đang định nghĩa các thành phần, quy trình sản xuất của sản phẩm, trong khi đó, trong đó việc sử dụng mỗi lần bản chất là ta sử dụng một sản phẩm do dây chuyền sản xuất ấy làm ra, chứ ta không phải sử dụng dây sản xuất ấy. Nó cũng tương tự với khái niệm class và object trong lập trình hướng đối tượng.

  • Scope là một khái niệm khá phực tạp và hiểu và sử dụng linh hoạt được scope là một điều không hề dễ dàng.

  • Về cơ bản thì , scope liên quan đến rất nhiều thành phần trong angularJs, tuy nhiên, quan hệ của nó trong controller thì có thể hiểu rằng, scope được nhúng vào controller, và mỗi một thể hiện của controller sẽ có một bản scope riêng và không đụng chạm nhau. hiểu nôm na như mỗi gói mì tôm đều chứa một gói gia vị vậy, tuy nhiên, mì tôm thì ta có thể sử dụng gia vị của gói này cho gói khác, còn scope của mỗi controller lại chỉ sử dụng trong controller đó mà thôi.

  • Một điều đáng chú ý là: mỗi app, chỉ có duy nhất một rootscope và mọi scope được nhúng vào một controller được tạo ra đều là con của rootscope. Tuy nhiên, có cơ chế nào để các scope con trong một app giao tiếp với nhau không?

  • Theo tìm hiểu của tôi là không, vì cơ bản, mọi giao tiếp giữa các scope trong một app đều phải thông qua rootscope, chúng được gọi là cơ chế chia sẻ giữa các scope con. Để dễ hình dung, ta có thể hiểu rằng, có hai người yêu nhau, một người ở châu phi và một người ở Việt Nam. người ở châu phi thường xuyên phải di chuyển, nên không có địa chỉ cố định, và thường xuyên không có cách nào liên lạc được, mọi sự liên lạc giữa hai người đề thông qua internet. oh, vậy đó, internet ở đây là rootscope, còn hai người đó như các scope được nhúng vào các thành phần khác của app. Mọi liên lạc giữa hai scope đều bắt buôc phải thông qua rootscope và không có cách nào để chúng qua mặt rootscope để đi "sầu riêng" với nhau cả.

Những cái hay của controller

  • Controller có thể kế thừa trong đó các controller con kế thừa mọi thuộc tính và phương thức của controller cha, và chúng (các controller) có thể định nghĩa lại các thành phần và thuộc tính đó. Nếu so sánh với các khái niệm trong lập trình hướng đối tượng thì , các thuộc tính và phương thức của lớp cha đều là protected.

  • Vê mặt hiểu cho dễ hiểu là bạn kế thừa một cách hoàn toàn từ cha bạn một khoản tài sản kếch xù, bạn có thể không dùng đến nó và để nó đó, dù sao nó cũng vẫn là của bạn, hoặc có thể tiêu hết hoặc làm nó lớn hơn thông qua việc mua sắm hay kinh doanh.

  • Controller có thể nested. Các lớp bên trong có thể gọi các thuộc tính bao chúng, nhưng các lớp bên ngoài thì lại không thể gọi các thuộc tính bao ở bên trong. Nói thì khá là khó hiểu, trong ví dụ mình sẽ show cụ thể.

Ví dụ thực tế

Binding data một chiều, từ model -> view

// dinh nghia mot controller
var myApp = angular.module('myApp',[]);
myApp.controller('GreetingController', ['$scope', function($scope) {
  $scope.greeting = 'Hola!';
}]);
<body ng-app="myApp">
    <div ng-controller="GreetingController"> {{greeting}}</div>
</body>

Kết quả trả về là

 Hola!

Binding data hai chiều, từ model -> view và view-> model

Vẫn với ví dụ controller trên, trên html chúng ta sẽ viết:

<body ng-app="myApp">
    <div ng-controller="GreetingController">
        <input type="text" ng-model="greeting" value ="{{greeting}}">
    </div>
</body>

Sau đó mọi sự thay đổi tại thẻ input sẽ được trực tiếp cập nhất vào thuộc tính greeting của GreetingController. Tuy nhiên, giá trị này chưa được lưu lại bất cứ đâu, nó chỉ tồn tại trong một session và lưu trữ tạm thời trong buffer, do đó, khi reset (ấn f5) lại trang, thì thuộc tính của greeting lần nữa trở về giá trị Hola.

Nested

Trong phát triển ứng dụng web, thì việc nested các form chúng ta thường phải gặp rất nhiều. tuy nhiên, ở đây cũng với một ý nghĩa tương tự, angular có thể sử dụng nested để định nghĩa và sử dụng các thành phần chúng.

// ta se dinh nghia 3 controller khac nhau
var app =  angular.module('myApp',[]);
app.controler("cat", ["$scope", function($scope){
    $scope.name = "cat";
}]);

app.controller("dog", ["$scope", function($scope){
    $scope.name1 = "dog";
}]);
app.controller("mouse", ["$scope", function($scope){
    $scope.name2 = "mouse";
}]);
// ta se goi thuoc tinh cuA 3 CONTROLLER
<body ng-app="myApp">
    <div ng-controller="cat">
        {{name}}{{name1}}{{name2}}
        <div ng-controller="dog">
            {{name}}{{name1}}{{name2}}
            <div ng-controller="mouse">
            {{name}}{{name1}}{{name2}}
            </div>
        </div>
    </div>
</body>

Kết quả nhận được sẽ là:

    cat
    cat dog
    cat dog mouse

=> Các controller bên trong có thể gọi các thuộc tính của controller bao chứa nó, nhưng những controller ở ngoài lại không thể gọi đến những thuộc tính bên trong mà nó bao chứa.

Inheritance Controller

        // Khai bao mot controller bat ky tai day
        app.controller("humanController", ["$scope", function($scope){
        $scope.attribute1 = "Human";
        // controller method
        $scope.say = function(){
        $scope.attribute1 = "Hello I'm a" + $scope.attribute1;
        };
        }]);

        // Ta khai bao them childController la con cua humanController
        app.controller('childController',['$scope', '$controller',function ($scope, $controller) {
            // child_scope = parent_scope;
            $controller("humanController", { $scope: $scope });
            // run ok
            // try re-define attribute
            $scope.say = function(){
                $scope.attribute = "YES> I am";
            }
        }]);

--> Các controller con kế thừa mọi thuộc tính của controller cha bao gồm các attribute và các method.

Các thuộc tính và các method của phương thức cha có thể định nghĩa theo cách phù hợp với các controller. Ứng dụng sự kế thừa của các controller cũng tương tự như khái niệm của sự kế thừa trong lập trình hướng đối tượng.

Note: Cần xem kết quả bạn chỉ cần thực hiện gọi attribute và method của lớp cha humanController và lớp con childController trên view, sẽ dễ dàng thấy được kết quả.