JavaScript là một trong những ngôn ngữ quan trọng và thông dụng nhất đang sử dụng trên thế giới. Xuất hiện gần 20 năm trước, JavaScript đã không ngừng phát triển. Từ một ngôn ngữ với rất nhiều lỗi, gây phiền phức cho người sử dụng như các popup JavaScript không mong muốn trên các trình duyệt, thì ngày nay ngôn ngữ JavaScript là sự chọn lựa cho lập trình web client-side. Các trình duyệt không những hỗ trợ JavaScript mà còn cung cấp các tính năng tìm lỗi, sửa code ở chế độ run-time. JavaScript ngày nay không chỉ xuất hiện trên các ứng dụng máy tính mà nó đang là nển tảng cho lập trình trên thiết bị cầm tay.
Tuy là một trong những ngôn ngữ mạnh nhất hiện nay nhưng lập trình bằng JavaScript lại thường gặp phải những vấn đề lớn về thời gian viết code, các file JavaScript dài dòng, khó đọc, khó quản lý. Đôi khi lập trình JavaScript cũng khá tự do, không theo một thiết kế hay kiến trúc nào nên dẫn đến khó kiểm tra, khó nâng cấp cũng như hạn chế khả năng tái sử dụng các code JavaScript. Một hạn chế rất lớn của JavaScript là Unit test. Viết Unit test Java là rất dễ nhưng để viết Unit test cho Javascript là cực kỳ khó.AngularJS được bắt đầu từ năm 2009, do lập trình viên Misko Hevery tại Google viết ra như là một dự án kiểu “viết cho vui”. Misko và nhóm lúc này đang tham gia vào 1 dự án của Google tên là Google Feedback. Với AngularJS, Misko đã rút ngắn số dòng code front-end từ 17000 dòng còn chỉ khoảng 1500. Với sự thành công đó, đội ngũ của dự án Google Feedback quyết định phát triển AngularJS theo hướng mã nguồn mở. Theo thông số từ Github mà mình thấy, hiện tại dự án AngularJS đang có hơn 34000 người theo dõi và hơn 14000 lượt fork.
Công nghệ HTML hỗ trợ tốt cho các trang web tĩnh, kiểu như trước năm 2000 vậy. Khi bạn xây dựng 1 trang web với JSP, PHP, Node/Express, hay Ruby thì nó cũng chỉ là một trang web tĩnh với nội dung được thay đổi khi bạn gửi request về máy chủ, máy chủ sẽ render 1 trang với nội dung tương ứng. Tuy nhiên mọi thứ đã thay đổi nhiều từ sự phát triển của HTML5, nhất là khi có sự chống lưng từ những ông lớn như Google, Yahoo, Facebook, và sự tập hợp đông đảo của cộng đồng mã nguồn mở.
Douglas Crockford, người được biết đến nhiều qua JSON và JSLint đã dùng sự chênh lệch về độ dày giữa 2 cuốn sách “Javascript: The definitive guide” và “Javascript: The good parts” để châm biếm 1 cách hài hước về JavaScript.
Tuy nhiên, sự thành công của jQuery đã khiến JavaScript được nhiều người yêu thích vì tính đơn giản và dễ sử dụng. Việc phát triển 1 website sử dụng AJAX thì không khó, bạn có thể dùng jQuery để làm việc này với $.ajax tuy nhiên làm thế nào để xây dựng một phần mềm có thể mở rộng, dễ test, nâng cấp và bảo trì thì không hề đơn giản vì bản thân JavaScript không được thiết kế ngay từ đầu để làm những việc này. Do đó sự ra đời của những framework hỗ trợ lập trình viên xây dựng ứng dụng web 1 cách có hệ thống đã ra đời như Sencha (cái này thì phải trả phí), Ember, Knockout, Backbone, và AngularJS.
Một câu hỏi là tại sao nó được gọi là AngularJS? Dấu cơ bản nhất trong HTML là “angle brackets” “<” và “>”. Có thể đoán Angular xuất phát từ “angle” bởi vì trong AnglarJS có một chức năng rất đặt biệt đó là khả năng mở rộng ngôn ngữ HTML.
Các đặc tính của AngularJS
- AngularJS là một Framwork phát triển dựa trên Javascript để tạo các ứng dụng web phong phú
- AngularJS thường dùng để phát triển frontend (giao diện khách hàng) thông qua các API để gọi data, sử dụng mô hình MVC rất mạnh mẽ
- Mã nguồn AngularJS tự động fix với các trình duyệt khác nhau nên bạn không cần phải lo vấn đề tương thích trình duyệt
- Angular là mã nguồn mở, hoàn toàn miễn phí và được phát triển bởi hàng ngàn các lập trình viên trên thế giới.
Chung quy lại có thể hiểu khi làm việc với AngularJS giống như là đang làm việc với Ajax, sử dụng cớ chế bind data, hoạt động theo mô hình MVC và sử dụng service để tương tác với dữ liệu từ server. Để rõ hơn thì chúng ta tìm hiểu các tính năng cố lõi của nó nhé.
Các tính năng code lõi của AngularJS
Sau đây là các tính năng cố lõi quan trọng trong AngularJS
- Data-binding: (liên kết dữ liệu) tự động đồng bộ dữ liệu giữa model và view
- Scope: (Phạm vi) Đây là những đối tượng kết nối giữa Controller và View
- Controller: Đây là những hàm javascript xử lý kết hợp với bộ điều khiển Scope
- Service: Như tôi đề cập ở trên, AngularJS sử dụng các API được xây dựng từ các web service (Java, PHP, ASP) để thao tác với DB.
- Filters: Bộ lọc lọc ra các thành phẩn của một mảng và trả về mảng mới
- Directives: đánh dấu vào các yếu tố của DOM, nghĩa là sẽ tạo ra các thẻ HTML tùy chỉnh
- Templates: hiển thị thông tin từ controller, đây là một thành phần của views
- Routing: chuyển đổi giữa các action trong controller
- MVC: Mô hình chia thành phần riêng biệt thành Model, View, Controller. Đây là một mô hình khá hay nhưng trong Angular thì nó được chế biến lại một chút gần giốn với MVVM (Model View View Model)
- Deep Linking: Liên kết sâu, cho phép bạn mã hóa trạng thái của ứng dụng trong các URL để nó có thể đánh dấu được với công cụ tìm kiếm.
- Dependency Injection: Angular giúp các nhà phát triển tạo ứng dụng dễ dàng hơn để phát triển, hiểu và thử nghiệm dễ dàng.
Những điểm nhấn trong lập trình AngularJS
1. Model –View – Controller:
AngularJS đã đưa ra mô hình MVC vào thiết kết client side nhằm mục đích sắp xếp lại cấu trúc của ứng dụng cũng như giúp cho thiết kế, testing ứng dụng được hiệu quả hơn. Nhưng mô hình MVC trong AngularJS không hoàn toàn giống với mô hình MVC truyền thống mà nó hướng về mô hình MVVM (Model-View-ViewModel) hơn.
Ví dụ 1:
VIEW
<tbody ng-app="Online" ng-controller="OrderController">
<tr ng-repeat="item in items ">
<td> {{item.name | uppercase}}</td>
<td> <input type=text ng-model="item.price" > </td>
<td> <input type=text ng-model="item.quantity"> /td>
<td>{{item.price * item.quantity |currency}}</td>
</tr>
<tr>
<td>Tong Cong</td>
<td>{{Sum() |currency}}</td>
</tr>
</tbody>
CONTROLLER
angular.module('Online', []);
function OrderController($scope) {
$scope.items = [
{ name: "TV", price: 1000, quantity: "1",
image: "\\Images\\tv.jpg" },
{ name: "Iphone 6", price: 1050, quantity:
"1", image: "\\Images\\Iphone.jpg" },
$scope.Sum = function () {
var total = 0; for (count = 0; count <
$scope.items.length; count++)
{ total += $scope.items[count].price *
$scope.items[count].quantity; }
return total;}
$scope.title = 'Online Shop';}
Từ ví dụ 1, chúng ta thấy rằng AngularJS đưa ra mô hình thiết kế rất rõ ràng:
- View: đơn thuần là một HTML template để hiển thị dữ liệu tới người sử dụng. Dữ liệu được lấy từ Model
- Model: là dữ liệu để hiển thị lên View, trong thí dụ 1, Model là “items”, “Sum”, “title”. Model chỉ là plain old JavaScript objects, không sử dụng setter/getter để truy cập dữ liệu. Điều này giúp cho người lập trình giảm đáng kể số lượng dòng code. Model được đóng gói trong controller.
- ViewMode: chính là “$scope” trong ví dụ 1. “$scope” là một JavaScript object rất quan trong trong mô hình MVC AngularJS bởi Controller và View là hoàn toàn độc lập nên “scope” là cầu nối, giữ liên kết giữa View và Controller. Nhiệm vụ của nó là phát hiện những thay đổi trong Model và thực thi các biểu thức liên quan đến những thay đổi này.
- Controller: nơi thực thi các business logic, được xem như là code behind của View, là nơi Model hoạt động.
AngulaJS MVC
MVC AngularJS không những giúp cho thiết kế Javascript được rõ ràng mà nó còn giúp cho phân công nhân sự trong dự án hoàn toàn độc lập giữa người phát triển UI và người xử lý business logic bên dưới.
2. Data-binding
Rất đơn giản, AngularJS sử dụng cú pháp: {{data}} để hiển thị giá trị của data trong DOM. Điều thú vị ở đây là kết quả mà data bạn cập nhật sẽ được hiển thị ngay tại chỗ chứ không cần thiết phải reload page:
Ví dụ:
1 2 3 4 | <div class = 'container' ng-app> <input type= 'text' ng-model= 'userName' /> <p>Hello {{userName}}!</p> </div> |
Khi bạn nhập tên trong input text, bạn sẽ thấy nội dung trong thẻ <p> được cập nhật ngay lập tức.
Một ví dụ nữa về khả năng này của AngularJS:
1 2 3 4 5 6 7 8 | <div class = 'container' ng-app> <div ng-controller= 'myController' > <form ng-submit= 'addNewUser()' > <input type= 'text' ng-model= 'NewUser' /> <input type= "submit" /> </form> <p>Hello {{NewUser}}!</p> <p>User Array: {{userList}}</p> |
1 2 3 4 5 | var myController = function ($scope){ $scope.userList = [ 'Rikky' ];</code></code>$scope.addNewUser = function (){ $scope.userList.push($scope.NewUser); }; } |
Mỗi lần bạn submit một user mới, user đó sẽ được push vào mảng userList. Thú vị là mọi sự thay đổi từ controller, ngay lập tức được DOM cập nhật, và ngược lại. Điều này được AngularJS đề cập như là một “dữ liệu chân lý duy nhất” (single-source-of-truth).
Khi bạn làm việc với các mô hình MVC framework khác:
(Ảnh: http://docs.angularjs.org)
Bạn sẽ rơi vào trường hợp model và view chỉ được ghép với nhau một lần duy nhất, vì vậy mọi sự cập nhật dữ liệu từ một trong hai lớp đó đều không liên quan gì đến lớp còn lại. Hệ quả là developer phải viết thêm phần “đồng bộ hóa” giữa hai lớp này.
Với AngularJS thì khác:
(Ảnh: http://docs.angularjs.org)
Quá trình đồng bộ luôn xảy ra giữa model và view. Mọi sự thay đổi dữ liệu trong lớp này, ngay lập tức được cập nhật vào lớp kia. Bạn chỉ cần “bind” một lần, và mọi thứ hoạt động trơn tru .
Cũng vì dữ liệu mà View hiển thị thực chất là sự ánh xạ từ Model, nên Controller cũng hoàn toàn không cần các thao tác data binding phức tạp và phụ thuộc vào cấu trúc DOM của View, kết quả là Controller của bạn sẽ không quan tâm xem View của bạn là gì. Và dĩ nhiên, logic giữa Controller và View được bóc tách rất rõ ràng. Code của bạn sẽ rất dễ để debug, viết unit-test. Còn gì hay ho hơn!
Thực ra là còn . Hãy tưởng tượng sự kết hợp hoàn hảo giữa AngularJS với web-socket, mọi thứ được cập nhật real time. Một thao tác cập nhật ở server xảy ra, ngay tức thì, nó sẽ được AngularJS ghi nhận và cập nhật lại Model của mình. Tiếp tục, đến lượt View, nó cũng sẽ cập nhật lại dữ liệu hiển thị của nó theo Model Welcome to real-time world!
Ví dụ:
Thực ra là còn . Hãy tưởng tượng sự kết hợp hoàn hảo giữa AngularJS với web-socket, mọi thứ được cập nhật real time. Một thao tác cập nhật ở server xảy ra, ngay tức thì, nó sẽ được AngularJS ghi nhận và cập nhật lại Model của mình. Tiếp tục, đến lượt View, nó cũng sẽ cập nhật lại dữ liệu hiển thị của nó theo Model Welcome to real-time world!
Ví dụ:
Ở đây mình dùng FireBase làm cơ sở dữ liệu. FireBase là một cơ sở dữ liệu online, realtime. Nó giúp bạn đọc, ghi dữ liệu trực tuyến:
1 2 3 4 5 6 7 8 9 10 11 12 | <div class = 'container' ng-app> <div ng-controller= 'myController' > <div> <div>Current message is:</div> <br/> <p class = 'well well-small' >{{msg}}</p> </div> <hr/> <form ng-submit= "updateMsg()" > <input type= "text" ng-model= 'newMsg' placeholder= 'Type your message' /> <input type= "submit" class = 'btn btn-primary pull-right' /> </form> |
1 2 3 4 5 6 7 8 9 10 | var myController = function ($scope){ $scope.msg = 'Loading...' ; messageRef.on( 'value' , function (snapshot){ $scope.msg = snapshot.val(); $scope.$apply(); // Thắc mắc chỗ này ư? Mình sẽ giải thích ngay sau đây! }); $scope.updateMsg = function (){ messageRef.set($scope.newMsg); }}; |
Bạn thử gõ một message mới, ngay tức thì dữ liệu sẽ được cập nhật.
(Nhớ đừng gõ cái gì linh tinh nhé )
(Nhớ đừng gõ cái gì linh tinh nhé )
Outer data-binding
Trước hết, bạn hãy thử giải quyết bài toán sau đây:
Trước hết, bạn hãy thử giải quyết bài toán sau đây:
“Tạo ra một đồng hồ đếm từ 1 đến 10 bằng AngularJS rồi lại quay về 1 và cứ thế tiếp tục?”
Dễ ợt: Bạn tạo một Model timer và dùng hàm setInterval để đếm đến 10, nếu đã bằng 10, set lại timer bằng 1.
Okay, đây là code:
http://jsfiddle.net/SSwtk/41/
http://jsfiddle.net/SSwtk/41/
1 2 3 4 5 6 | <div class = 'container' ng-app> <div ng-controller= 'myController' > <h2>{{timer}}</h2> <button class = 'btn btn-primary' ng-click= 'count10()' >Start count to 10</button> </div> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 | var myController = function ($scope) { $scope.timer = 1; $scope.count10 = function () { setInterval( function () { if (10 > $scope.timer) { $scope.timer++; } else { $scope.timer = 1; } }, 1000); }; }; |
Test chưa? Chưa test thì test đi rồi hãy đọc tiếp! Nhé!
.
.
Okay, nếu bạn đã test xong, bạn sẽ thấy:
NÓ KHÔNG HOẠT ĐỘNG
Và đấy chính là lý do mình đề cập đến mục này: Outer Data-binding.
.
.
Okay, nếu bạn đã test xong, bạn sẽ thấy:
NÓ KHÔNG HOẠT ĐỘNG
Và đấy chính là lý do mình đề cập đến mục này: Outer Data-binding.
Phân tích hàm count10 của bạn, bạn sẽ thấy thao tác cập nhật giá trị của timer nằm trong một callback!
1 2 3 4 5 6 7 | setInterval( function () { if (10 > $scope.timer) { $scope.timer++; } else { $scope.timer = 1; } }, 1000); |
Tức là nó sẽ thực hiện ở NGOÀI ANGULARJS.
Vô lý! Rõ ràng là tôi viết nó trong một hàm nằm trong Controller cơ mà!
Đúng, bạn “viết nó trong Controller” . Nhưng đúng hơn thì bạn “định nghĩa nó trong controller”. Còn nó sẽ được call ở ngoài. Vì setInterval là một function ở ngoài AngularJS. Chính vì lý do này, Model của bạn có thay đổi giá trị, thì AngularJS cũng không thể bắt được sự kiện thay đổi giá trị đó.
Với ví dụ trên, nếu bạn ấn nút “Start count to 10″ liên tục, bạn sẽ thấy giá trị có thay đổi, nhưng đấy là do sự kiện “ng-click” của nút bấm phát ra! Và, dù sao thì nó cũng không phải là mục đích của bài toán.
Làm sao đây?
Rất may là AngularJS có cung cấp một phương thức: $apply(), nhớ là có dấu ‘$’ nhé! Nó giúp bạn phát ra sự kiện TRONG AngularJS khi mà giá trị của $scope bị thay đổi ở NGOÀI AngularJS. Tất cả những gì bạn cần làm là gọi phương thức này ở đoạn code làm thay đổi giá trị của $scope:
Đúng, bạn “viết nó trong Controller” . Nhưng đúng hơn thì bạn “định nghĩa nó trong controller”. Còn nó sẽ được call ở ngoài. Vì setInterval là một function ở ngoài AngularJS. Chính vì lý do này, Model của bạn có thay đổi giá trị, thì AngularJS cũng không thể bắt được sự kiện thay đổi giá trị đó.
Với ví dụ trên, nếu bạn ấn nút “Start count to 10″ liên tục, bạn sẽ thấy giá trị có thay đổi, nhưng đấy là do sự kiện “ng-click” của nút bấm phát ra! Và, dù sao thì nó cũng không phải là mục đích của bài toán.
Làm sao đây?
Rất may là AngularJS có cung cấp một phương thức: $apply(), nhớ là có dấu ‘$’ nhé! Nó giúp bạn phát ra sự kiện TRONG AngularJS khi mà giá trị của $scope bị thay đổi ở NGOÀI AngularJS. Tất cả những gì bạn cần làm là gọi phương thức này ở đoạn code làm thay đổi giá trị của $scope:
1 2 3 4 5 6 7 8 9 10 11 12 13 | var myController = function ($scope) { $scope.timer = 1; $scope.count10 = function () { setInterval( function () { if (10 > $scope.timer) { $scope.timer++; } else { $scope.timer = 1; }; $scope.$apply(); //<-- Add this }, 1000); }; }; |
Và đây là kết quả:
http://jsfiddle.net/SSwtk/43/
Bây giờ thì hết thắc mắc đoạn code ở phần sử dụng Firebase chưa?
http://jsfiddle.net/SSwtk/43/
Bây giờ thì hết thắc mắc đoạn code ở phần sử dụng Firebase chưa?
Một trong những vấn đề khó chịu của Angular khi model bị thay đổi ngoài Angular, bạn phải dùng $scope.$apply() để cập nhật. Dưới đây là 1 cách làm tương tự trong Angular
1 2 3 4 5 6 7 8 9 10 | $scope.count10Angular = function () { $timeout( function () { if (10 > $scope.timer) { $scope.timer++; } else { $scope.timer = 1; } $scope.count10Angular(); }, 1000); }; |
Để dùng đc $timeout bạn phải inject nó vào controller
3. Sự linh hoạt của Filter, Sort
AngularJS Filter được sử dụng để trích dữ liệu và định dạng dữ liệu. Người lập trình chỉ nói cho AngujarJS biết tôi đang cần cái gì hơn là phải đi làm công việc đó. Ví dụ 1 có những từ đầy đủ ý nghĩa, dể hiểu, đi sau kí tự “|” như là “currency”, “uppercase” chính cách để AngularJS định dạng dữ liệu trên UI như kiểu tiền tệ <td>{{item.price * item.quanlity |currency}}</td>, chữ hoa <td>{{item.name | uppercase}}</td>. Khi nhìn vào code, chúng ta có thể hiểu ngay mục đích và ý nghĩa của câu lệnh. Ngoài những kiểu định dạng chuẩn như “lowercase”, “number”, AngularJS hỗ trợ người lập trình xây dựng riêng những kiểu định đạng kiểu dữ liệu khác như datetime, hay thay thế các ký tự đặt biệt...
Ví dụ 3 - tạo một custom filter thay thế ký tự đặc biệt trong chuỗi:
Filter.js
var app = angular.module('Online', []);
app.filter('replaceSpecialCharacter', function () {
return function (name) {
return name.replace(/[^\w\s]/gi, '');
};});
Filter.JS định nghĩa một hàm “'replaceSpecialCharacter'” dùng thay thế ký tự đặt biệt thành khoảng trắng. Bất kỳ dữ liệu nào trên UI cần thay thế các ký tự đặc biệt, chỉ cần đặt “'replaceSpecialCharacter'” sau chuỗi như <td> {{item.name|'replaceSpecialCharacter'} </td>. Các file filter chỉ là các POJO class, nên nó hoàn toàn có thể xây dựng thành thư viện cho các project sau.
Trích dữ liệu và sắp sếp dữ liệu trong AngularJS cũng cực kỳ đơn giản và rất hiệu quả. Người lập trình cũng chỉ nói cho AngularJS biết tôi muốn trích dữ liệu gì và sắp sếp field nào, theo thứ tự tăng hay giảm. Từ ví dụ 1, giả sử cần có chức năng tìm kiếm và sắp sếp theo “price”, trong phần View chỉ cần thêm vài dòng code như thí dụ dưới đây:
Ví dụ 4 - Tìm kiếm và sắp sếp dữ liệu:
<tr>
<td>Tim Kiem Ma Hang: <input ng-model="search"></td>
</tr>
<tr> <td> Sap xep theo Gia:
<select ng-model="SortPrice">
<option value="">-- Sap xep --</option>
<option value="price">Tang</option>
<option value="-price">Giam</option>
</select>
</td></tr>
<tr ng-repeat=" item in items
|filter:search|orderBy:SortPrice">
<td>{{item.name}}</td>
…
</tr>
Text box search <input ng-model="search> để nhập vào dữ liệu tìm kiếm và một combox box sắp xếp theo “price” tăng/giảm dần <select ng-model="SortPrice">. Dòng lệnh <tr ng-repeat=" item in items |filter:search|orderBy:SortPrice"> có thêm hai lệnh “filter:search” và “orderBy:SortPrice”. Lệnh “filter:search” yêu cầu AngularJS tìm kiếm dữ liệu theo text box <input ng-model="search">. Ở đây, không nói rõ tìm kiếm theo field nào nên AngularJS sẽ tìm tất cả các field trong Model. Nếu yêu cầu AngularJS tìm kiếm theo field “name”, chỉ cần thêm “name” vào sau Model “search”: “<input ng-model="search.name">”. Hoàn toàn không có một đoạn code JavaScript nào ở View hay Controller. Nếu mỗi màn hình đều có yêu cầu chức năng tìm kiếm, chúng ta sẽ tiết kiệm một lượng lớn thời gian lập trình và test. Tương tự như tìm kiếm, sắp sếp dữ liệu cũng chỉ cần gọi lệnh “orderBy:SortPrice” trong đó “SortPrice” trên View là một combox <ng-model="SortPrice"> chỉ đến Model “price”. Chú ý rằng khi muốn sắp sếp theo giảm dần, dấu ‘-‘ phải được thêm trước field <option value="-price">Giam</option>. Như vậy xem như tất cả các công việc tìm kiếm, sắp xếp dữ liệu trong Model đã được AngularJS hỗ trợ. Người lập trình chỉ yêu cầu AngularJS làm gì mà thôi. Tương tự như định dạng dữ liệu, người lập trình có thể viết riêng cho dự án các hàm sắp xếp hay tìm kiếm tùy theo yêu cầu từng dự án.
4. Directives
4. Directives
Không thể bàn cãi về sức mạnh của ngôn ngữ HTML, nhưng HTML bó buộc người lập trình phải theo cú pháp của nó. Thí dụ như lệnh tạo button có cú pháp <button id="id" class="Default"> thì mỗi trang muốn tạo button đều phải tuân theo cú pháp này, các ứng dụng khác cũng phải như thế. Trang thứ hai không thể sử dụng lại button đã định nghĩa ở trang thứ nhất. Người lập trình không thể định nghĩa các syntax HTML element/ attribute mới như là <save-Order></save-Order> ,<print-Invoice></print-Invoice>, <div view-Order></div> đúng với mục đích, ngữ cảnh của mỗi button. Những vấn đề này được AngularJS giải quyết thông qua kỹ thuật gọi là Directives. Nói các khác, Directives cho phép người lập trình mở rộng ngôn ngữ HTML, chỉ cho HTML các cấu trúc mới của DOM. Nhìn vào các thí dụ trên chúng ta thấy rằng có nhiều attribute không thuộc HTML trước đây như là “ng-model”, “ng-repeat”, “ng-controller”. Đó chính là các Directive có sẵn và AngularJS hỗ trợ cho người lập trình tạo ra các Directives tương tự như thế. Bốn loại trong cấu trúc DOM mà người lập trình can thiệp vào đó là: E = element , A = attribute, C = class, M = comment. Mỗi cách định nghĩa trên tương ứng với cách gọi trong HTML như là <save-Order></save-Order><div save-Order></div>, <div class="save-Order"></div>, <!-- directive: save Order -->. Ví dụ dưới đây tạo ra một Directive button:
Ví dụ 5: Directive hiển thị giỏ hàng
Directives.JS
// Directive cartItems: hiển thị giỏ hàng
directive('cartItems', function () {
return {
replace: true, restrict: 'EA',
templateUrl: 'SelectedCart.html'
};});
Cách gọi Directive trên View
<body ng-controller="OrderController">
<div id="main">
<div><cart_Items></cart_Items></div>
</div>
</body>
SelectedCart.html
<div><img src="..\\Images/cart.png" alt="Items: "/>
<span>( {{selectedItems.length}} )</span></div>
Trong file Directives.JS, định nghĩa một directive mới “cartItems” với từ khóa “restrict: 'EA'” nên nó vừa là Element vừa là Attribute. “templateUrl” chỉ đến file HTML template là nội dung của Directive. Mục đích của directive này là hiển thị hình giỏ xe hàng và số lượng hàng hóa đang được chọn nên nội dung của template 'SelectedCart.html' là các syntax hiển thị hình ảnh và số lượng item được chọn. View chỉ gọi tên directive này <div><cart_Items></cart_Items></div> để hiển thị giỏ hàng. Directive rất hay bởi vì ngoài cách viết code theo kiểu khai báo làm cho mã code rất dễ đọc, nó còn cho phép người thiết kế có thể thiết kế các trang web mà không cần phải biết code và quan trọng là các directive này hoàn toàn độc lập nên có thể sử dụng lại cho dự án khác hay chia sẽ cho cộng đồng. Khi xây dựng Directive cần chú ý là không nên sử dụng tiếp đầu ngữ là “ng-” đặt tên cho custom Directive mới gì nó sẽ gây nhầm lẫn với các Directive của AngularJS và cách đặt tên phải theo chuẩn camel-cased. Thí dụ Directive “cartItems”, trong HTML, chúng ta phải thêm ký tự “-“ vào thành “cart_Items”.
5. HTML Template
Trong thí dụ 5, AngularJS sử dụng một html template 'SelectedCart.html' để hiển thị nôi dung trên View. Html template giúp cho người lập trình giải quyết những yêu cầu về UI “động” rất nhanh như trên cùng một tập dữ liệu, nếu là nhà quản lý, thì dữ liệu sẽ hiển thị đầy đủ nhưng nếu nhân viên thì dữ liệu hiển thị sẽ ít hơn. Hay cũng trên 1 tập dữ liệu, nếu mặt hàng là TV thì phải hiển thị cột “Mô tả” là một file video, nhưng các hàng khác chỉ hiển thị là text. Cách đơn giản nhất có lẽ là sử dụng 3 Directive “ng-repeat”, “ng-switch on” và “ng-switch-when” có cấu trúc như là vòng lập “for … switch .. case” để gọi đúng template ứng với giá tri của Model.
Ví dụ 6 - dynamic template:
<div ng-repeat="item in items">
<div ng-switch on="item.name">
<div ng-switch-when="TV" >
<div template_Video div>
</div>
<div ng-switch-when="Laptop">
<div template_Text</div>
</div></div></div>
7. Dependency Injection (DI)
Đây có lẽ là kỹ thuật hay nhất của AngularJS. DI là một design pattern, có thể hiểu đơn giản cách làm giảm sự phụ thuộc giữa các object khi xây ứng dụng. Nếu thiết kế một object A phụ thuộc vào nhiều object khác thì dẫn tới object A sẽ khó được quản lý cũng như khó Unit test. Thường mỗi dòng lệnh có thể phụ thuộc vào một object hay một library. Giả sử Object A sử dụng Object B thì Object B có thể khai báo là global, hay sử dụng “new” trong Object A để tạo new instance của Object B. Như thế, giữ Object A và Object B luôn luôn có sự liên kết lẫn nhau. Cách thứ 3 là gắn (inject) Object B tới Object A ở khi cần thiết ở run time. Đó là cách mà AngularJS hướng tới để giải quyết vấn đề giảm sự phụ thuộc giữa các Object. Xem thí dụ đơn giản dưới đây để hiểu về AngularJS.
Ví dụ 7 -custom DI:
Service.JS
module.service('OrderService', function () {
var Orders = [{
id: 0,'name': 'TV','price': 1000,
'quantity': '1', image: "\\Images\\tv.jpg"
}];
//save new order
this.save = function (order) {}
}});
Controller
var module = angular.module('Online', []);
module.controller('OrderController', function ($scope, OrderService) {
// Save Order
$scope.saveOrder = function () {
OrderService.save($scope.newOrder);
}})
File “OrderService” bao gồm các hàm business logic “save”, “delete”... Controller “OrderController” sử dụng “OrderService” nhưng không có bất cứ biến toàn cục hay từ khóa “new” ở “OrderController” . Khi UI gọi hàm "SaveOrder” của “OrderController”, AngularJS sẽ tự động gắn “OrderService” vào “OrderController”. Do AngularJS quản lý theo tên, nên gì một lý do nào đó, controller không sử dụng “OrderService” mà chuyển sang một service khác, thì người lập trình chỉ đổi tên mà thôi. Giả sử ứng dụng đang trên Production bị lỗi một hàm nào đó, người lập trình viết một service mới cho mục đích tìm lỗi. Khi tìm lỗi hoàn tất, chúng ta trả đúng lại tên cũ. Ưu điểm của DI là khi thiết kế, người lập trình chia nhỏ các business logic từ phức tạp thành nhiều phần đơn giản. Càng chia nhỏ, code càng dể quản lý và sẽ giúp cho việc thự hiện Unit test trở nên thuận lợi hơn rất nhiều.
7. Unit Test
Unit test JavaScript là công việc vô cùng phức tạp và mất nhiều thời gian. Nếu các hàm JavaScript phụ thuộc lẫn nhau càng nhiều thì việc Unit test càng khó. AngularJS xây dựng trên ý tưởng DI và làm giảm sự phụ thuộc giữa các component, tách riêng logic và view cho nên thực hiện unit test rất thuận lợi. Tất cả ví dụ trên về filter, directives, controller, DI là những file riêng biệt cho nên người lập trình dễ dàng viết các Unit test cho từng loại như là NUnit của .Net. Hiện nay Jasmine là một công cụ rất hay để thực hiện unit test JavaScript. Sử dụng Jasmine rất đơn giản, chỉ cần download jasmine package và viết test script như thí dụ 8 dưới đây:
Ví dụ 8 - unit test filter ví dụ 3:
index.html.spec
describe('Testing', function() {
var specialCharacterFilter;
beforeEach(module('app'));
beforeEach(inject(function($filter) {
specialCharacterFilter= $filter('replaceSpecialCharacter');
}));
it('should remove special character', function() {
var name= 'TV%^/';
var nameExpected = 'TV';
expect(specialCharacterFilter(name)).toBe(nameExpected);
});});
Unit test cho trường hợp ĐÚNG: giá trị đầu vào là “ name= 'TV%^/'” và giá trị mong muốn sau khi thực thi hàm là nameExpected = 'TV', khi chạy unit test kết quả ĐÚNG.
Unit test cho trường hợp SAI. |
Thay thế giá trị mong muốn thành nameExpected = 'TV123' khi chạy unit test kết quả SAI. |
Ưu điểm và nhược điểm của AngularJS
1.Ưu điểm:
- Angular cho phép tạo ra các ứng dụng một cách đơn giản, code sạch
- Angular sử dụng data bind giống .NET với tính năng liên kết với HTML nên giúp người dùng cảm thấy dễ chịu.
- Angular đang ở giai đoạn thử nghiệm
- Angular có thể chạy trên hầu hết các trình duyệt điện thoại thông minh.
2.Nhược điểm:
Mặc dù angular có nhiều lợi thế ưu điểm nhưng nó cũng có mặt trái ngược lại:
- Không an toàn: Được phát triển từ javascript nên nó không an toàn, phía máy chủ phải thường xuyên xác nhận quyền để hệ thống chạy trơn tru.
- Phụ thuộc: Nếu người dùng vô hiệu hóa javascript thì coi như đi tong con long bong.
Kết luận
Hiện nay, có nhiều JavaScript framework như Backbone, Ember, AngularJS giúp người lập trình có thể tiết kiệm rất nhiều thời gian viết code. Không công bằng khi nói một framework tốt hơn những cái còn lại.
Tất cả đều có ưu, khuyết điểm riêng, quan trọng là những chức năng của framework được chọn đáp ứng nhu cầu của dự án. Nhưng AngularJS có những tính năng rất đặc biệt như dependency injection giúp việc Unit test được dễ dàng hay Directives giúp cho người lập trình có thể chia nhỏ JavaScript vào các template. Do AngularJS tập trung vào khả năng test và chất lượng code cho nên việc có một thiết kế phù hợp cho dự án từ ban đầu là rất cần thiết. Hy vọng rằng sau những cải tiến tiếp theo thì AngularJS sẽ là một trong những framework mà người lập trình sẽ chọn lựa cho các ứng dụng web.
Klik untuk melihat kode: :) =( :s :D :-D ^:D ^o^ 7:( :Q :p T_T @@, :-a :W *fck* x@