Controller trong AngularJS
AngularJS là khung ứng dụng xây dựng theo mô hình MVC (Model - View - Controller), trong đó thành phần Controller đóng vai trò điều phối tương tác của người dùng đồng thời chịu trách nhiệm trong việc tạo và quản lý Model (định nghĩa bên trong Scope) và gắn kết Scope với View. Bài viết này trình bày về vai trò của Controller trong AngularJS và mối quan hệ của nó với các thành phần còn lại (Scope và View).
Controller trong AngularJS
Controller trong AngularJS là một đối tượng JavaScript dùng để hỗ trợ Scope trong việc kiểm soát dữ liệu. Khi một Controller được gắn vào DOM (Document Object Model) thông qua chỉ thị ng-controller thì Angular sẽ tạo ra đối tượng Controller.
Scope được xem như là kết nối giữa View và Controller. Trong AngularJS thì các đối tượng hay biến bắt đầu bằng $. $scope là đối tượng của Scope.
Controller được sử dụng để thiết lập các trạng thái ban đầu của $scope và thêm các hành vi cho đối tượng $scope.
Controller có thể kết hợp với $scope thông qua chỉ thị ng-controller hoặc $route service.
Controller và View chia sẻ một đối tượng dùng chung là Scope. Thông thường Controller gắn giá trị cho các thuộc tính trên Scope, View sử dụng các thuộc tính này cho việc hiển thị. Angularjs chịu trách nhiệm cho việc đồng bộ 2 phía (two-way data binding) nghĩa là bất cứ sự thay đổi được thực bởi người dùng trên View thì giá trị Model tương ứng sẽ thay đổi theo và ngược lại, khi giá trị Model thay đổi thì View cũng thay đổi theo.
Khai báo Controller
<ANY ng-controller="">
...
</ANY>
Ví dụ minh họa
File exampleController.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example Controller</title>
</head>
<body>
<script src="js/angular.js"></script>
<script src="js/angular-animate.js"></script>
<script src="js/angular-route.js"></script>
<script src="js/angular.min.js"></script>
<script src="js/javascript/MyController.js"></script>
<div ng-app="myApp" ng-controller="MyController">
<p>Hello :</p> <input type="text" ng-model="name">
<p> {{name}} </p>
</div>
</body>
</html>
File MyController.js
var myApp = angular.module('myApp',[]);
myApp.controller('MyController', ['$scope', function($scope){
$scope.name = "Dong Diem Thuy";
}]);
Kết quả hiển thị
Ví dụ trên chỉ có một Controller là MyController, một thành phần input dùng để hiển thị và có thể thay đổi được bởi người dùng và một biểu thức hiển thị giá trị của $scope.name. Ban đầu Controller thiết lập giá trị $scope.name,View sẽ hiển thị tới người dùng giá trị này, khi người dùng thay đổi trên View thì giá trị Model tương ứng lập tức thay đổi theo.
Từ đó ta có thể thấy rằng Controller làm được việc là gán giá trị mặc định cho Model để hiển thị tới người dùng.
Kế thừa Scope trong AngularJS
Khi một chỉ thị ng-controller tạo ra một Scope con mới, thì các Scope có thể kế thừa nhau. $scope tương ứng với một Controller có thể truy cập vào phương thức định nghĩa bởi Controller ở cấp cao hơn. Xem thêm Angular Scope Inheritance.
Ví dụ minh họa cho các Controller lồng nhau.
File NestedController.html
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
<script src="js/angular.js"></script>
<script src="js/angular-animate.js"></script>
<script src="js/angular-route.js"></script>
<script src="js/angular.min.js"></script>
<script src="js/javascript/Nested.js"></script>
<style>
div.nested div {
padding: 10px;
border: solid 2px red;
}
</style>
</head>
<body ng-app="myApp">
<div class="nested" ng-controller="MyCtrl">
<label> Name </label> <input type="text" ng-model="name">
<div class="nested" ng-controller="NestedMyCtrl">
<label> Name </label> <input type="text" ng-model="name">
<div class="nested" ng-controller="NestedMyCtrl1">
<label> Name </label> <input type="text" ng-model="name">
</div>
</div>
</div>
</body>
</html>
File Nested.js
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', ['$scope', function($scope) {
$scope.name = "Marry";
}]);
myApp.controller('NestedMyCtrl', ['$scope', function($scope) {
}]);
myApp.controller('SubNestedMyCtrl', ['$scope', function($scope) {
}]);
Kết quả hiển thị:
Ví dụ trên chỉ có ba Controller là MyCtrl, NestedMyCtrl, SubNestedMyCtrl, một thành phần input dùng để hiển thị và có thể thay đổi được bởi người dùng và một biểu thức hiển thị giá trị của $scope.name. Ban đầu Controller thiết lập giá trị $scope.name, View sẽ hiển thị tới người dùng giá trị này, mặc định là các Controller con NestedMyCtrl, SubNestedMyCtrl sẽ mang giá trị mặc định là Marry theo Controller MyCtrl. Khi thay đổi tên $scope.name ở MyCtrl thì ở NestedMyCtrl và SubNestedMyCtrl cũng sẽ thay đổi theo, nếu thay đổi ở NestedMyCtrl thì chỉ có SubNestedMyCtrl thay đổi theo, có nghĩa là các Controller cha sẽ không bị thay đổi nếu thay đổi các Controller con.
Testing Controller
Thực hiện viết Unit Test cho Controller sử dụng Jasmine và angular-reed. Jasmine được dùng để mô tả thông số kĩ thuật beforeEach và afterEach để thiết lập và ngắt các mã lệnh.
Sử dụng Inject để khởi tạo các Scope và Controller. Chúng ta không thể khởi tạo 1 Scope là một đối tượng Javascript khi không thể $watch nó. Các $digest được gọi để kích hoạt một lệnh sau khi đã thay đổi scope.
describe('MyCtrl', function(){
var scope, ctrl;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
ctrl = $controller(MyCtrl, { $scope: scope });
}));
it('should change greeting value if name value is changed', function() {
scope.name = "Frederik";
scope.$digest();
expect(scope.greeting).toBe("Greetings Frederik");
});
});
Tài liệu tham khảo
Các bạn có thể tải mã nguồn của ví dụ Controller lồng nhau để chạy thử.