12/08/2018, 17:10

Working with Forms AngularJS

AngularJS là một phần gắn bó với công việc hàng ngày của mình, hôm nay mình muốn chia sẻ một vài kiến thức về Forms trong AngularJS theo cách nhìn nhận mà mình đọc được trong sách, hy vọng giúp ích cho mọi người. Preparing the Example Project Đầu tiên chúng ta sẽ có một file html là forms.html ...

AngularJS là một phần gắn bó với công việc hàng ngày của mình, hôm nay mình muốn chia sẻ một vài kiến thức về Forms trong AngularJS theo cách nhìn nhận mà mình đọc được trong sách, hy vọng giúp ích cho mọi người.

Preparing the Example Project

Đầu tiên chúng ta sẽ có một file html là forms.html

<!DOCTYPE html>
<html ng-app="exampleApp">
  <head>
    <title>Forms</title>
    <script src="angular.js"></script>
    <link href="bootstrap.css" rel="stylesheet" />
    <link href="bootstrap-theme.css" rel="stylesheet" />
    <script>
      angular.module("exampleApp", [])
      .controller("defaultCtrl", function ($scope) {
      $scope.todos = [
      { action: "Get groceries", complete: false },
      { action: "Call plumber", complete: false },
      { action: "Buy running shoes", complete: true },
      { action: "Buy flowers", complete: false },
      { action: "Call family", complete: false }];
      });
    </script>
  </head>
  <body>
    <div id="todoPanel" class="panel" ng-controller="defaultCtrl">
      <h3 class="panel-header">
        To Do List
        <span class="label label-info">
        {{(todos | filter: {complete: 'false'}).length}}
        </span>
      </h3>
      <table class="table">
        <thead>
          <tr>
            <th>#</th>
            <th>Action</th>
            <th>Done</th>
          </tr>
        </thead>
        <tr ng-repeat="item in todos">
          <td>{{$index + 1}}</td>
          <td>{{item.action}}</td>
          <td>{{item.complete}}</td>
        </tr>
      </table>
    </div>
  </body>
</html>

Có thể thấy chúng ta đang dùng một span element để hiển thị số lượng của complete to-do items bằng Boostrap label như sau:

...
<span class="label label-info">{{(todos | filter: {complete: 'false'}).length}}</span>
...

Ở dòng trên inline data binding chúng ta đã sử dụng filter để select các đối tượng to-do mà nó có thuộc tính complete là false.

Using Form Elements with Two-Way Data Bindings

Trước khi đi sâu vào chi tiết của các directive mà AngularJS cung cấp để làm việc với forms, chúng ta hãy xem lại một chút về cơ chế two-way data bindings(một cơ chế rất quen thuộc với những người làm việc với AngularJS),nó liên quan tới form elements vì nó có thể thu thập dữ liệu từ user sau đó cập nhật model.Ở đây cơ chế two-way data bindings được tạo ra bởi ng-model directive, nó có thể apply bất kì form elements bao gồm cả input.AngularJS đảm bảo rằng các thay đổi trong form element được tự động update vào phần tương ứng của data model.

...
<table class="table">
  <thead>
    <tr>
      <th>#</th>
      <th>Action</th>
      <th>Done</th>
    </tr>
  </thead>
  <tr ng-repeat="item in todos">
    <td>{{$index + 1}}</td>
    <td>{{item.action}}</td>
    <td>
      <input type="checkbox" ng-model="item.complete"
    </td>
  </tr>
</table>
...

Một to-do list mà không cho phép bạn kiểm tra các items không phải là điều tốt cho bạn sử dụng.Trong danh sách trên, chúng ta đã thay thế inline data binding hiển thị giá trị của thuộc tính hoàn chỉnh với một check box input element.Chúng ta đã sử dụng ng-model để tạo two-way binding với thuộc tính complete.Khi trang được load xong, AngularJS sử dụng thuộc tính complete để set trạng thái khởi tạo của check box và khi user check hoặc uncheck thì giá trị được tự động update.Có thể thấy ng-model directive là một cách rất gọn nhẹ để áp dụng một trong các tính năng của AngularJS data-binding theo cách cho phép người dùng sửa đổi các mô hình dữ liệu.

Bạn có thể thấy hiệu quả của việc thay đổi mô hình dữ liệu bằng cách check và uncheck các input elements trong ví dụ.Để thấy model đang được thay đổi bạn cần nhìn vào label hiển thị số lượng các to-do items có trường complete là false.

Implicitly Creating Model Properties

Ví dụ trước hoạt động trên thuộc tính model mà chúng ta đã xác định rõ khi ta setup controller, nhưng bạn cũng có thể sử dụng two-way data bindings để ngầm tạo các thuộc tính trong data model-một tính năng hữu ích khi bạn sử dụng form elements để thu thập dữ liệu người dùng để tạo một đối tượng hoặc thuộc tính mới trong data model.Chúng ta cùng phân tích ví dụ sau để hiểu rõ hơn

<html ng-app="exampleApp">
  <head>
    <title>Forms</title>
    <script src="angular.js"></script>
    <link href="bootstrap.css" rel="stylesheet" />
    <link href="bootstrap-theme.css" rel="stylesheet" />
    <script>
      angular.module("exampleApp", [])
      .controller("defaultCtrl", function ($scope) {
      $scope.todos = [
      { action: "Get groceries", complete: false },
      { action: "Call plumber", complete: false },
      { action: "Buy running shoes", complete: true },
      { action: "Buy flowers", complete: false },
      { action: "Call family", complete: false }];
       
      $scope.addNewItem = function (newItem) {
      $scope.todos.push({
      action: newItem.action + " (" + newItem.location + ")",
      complete: false
      });
      };
      });
    </script>
  </head>
  <body>
    <div id="todoPanel" class="panel" ng-controller="defaultCtrl">
      <h3 class="panel-header">
        To Do List
        <span class="label label-info">
        {{ (todos | filter: {complete: 'false'}).length}}
        </span>
      </h3>
      <div class="row">
        <div class="col-xs-6">
          <div class="well">
            <div class="form-group row">
              <label for="actionText">Action:</label>
              <input id="actionText" class="form-control"
                ng-model="newTodo.action">
            </div>
            <div class="form-group row">
              <label for="actionLocation">Location:</label>
              <select id="actionLocation" class="form-control"
                ng-model="newTodo.location">
                <option>Home</option>
                <option>Office</option>
                <option>Mall</option>
              </select>
            </div>
            <button class="btn btn-primary btn-block"
              ng-click="addNewItem(newTodo)">
            Add
            </button>
          </div>
        </div>
        <div class="col-xs-6">
          <table class="table">
            <thead>
              <tr>
                <th>#</th>
                <th>Action</th>
                <th>Done</th>
              </tr>
            </thead>
            <tr ng-repeat="item in todos">
              <td>{{$index + 1}}</td>
              <td>{{item.action}}</td>
              <td>
                <input type="checkbox" ng-model="item.complete">
              </td>
            </tr>
          </table>
        </div>
      </div>
    </div>
  </body>
</html>

Các HTML elements mới trong ví dụ này trong có vẻ phức tạp hơn nhiều vì Boostrap class chúng ta đã applied để get layout chúng ta muốn có.Trong thực tế, tất cả những gì chúng ta quan tâm là các input element.

<input id="actionText" class="form-control" ng-model="newTodo.action">

và select element:

<select id="actionLocation" class="form-control" ng-model="newTodo.location">
  <option>Home</option>
  <option>Office</option>
  <option>Mall</option>
</select>

Cả 2 đều sử dụng ng-model directive, được configure để update thuộc tính model mà chúng ta chưa xác định rõ ràng: thuộc tính newTodo.action và newTodo.location.Những thuộc tính này không phải là một phần của miền model của chúng ta, nhưng chúng ta cần để truy cập giá trị mà người dùng nhập vào để sử dụng trong hành vi addNewItem chúng ta đã xác định trong controller và gọi nó khi người dùng nhấp chuột vào button element.

$scope.addNewItem = function (newItem) {
  $scope.todos.push({action: newItem.action + " (" + newItem.location + ")",
    complete: false
  });
};

Hành động của controller là một function chiếm một object với action và location properties và thêm mới một object vào mảng to-do items.Bạn có thể thấy làm thế nào để pass qua các newTodo object để apply button element.

<button class="btn btn-primary btn-block" ng-click="addNewItem(newTodo)">
  Add
</button>

newTodo object và thuộc tính action, location của nó không tồn tại khi trang forms.html được load lần đầu tiên bởi trình duyệt.Chỉ dữ liệu trong model được set bởi to-do items tồn tại mà chúng ta đã hard-coded trong controller factory function.AngulaJS sẽ create newTodo object một cách tự động khi input hoặc select element được thay đổi và assign một value tới action của nó hoặc location properties dựa trên mỗi element mà user đang tương tác với nó.

Do tính linh hoạt này, AngularJS có một cái nhìn khá dễ chịu vể trạng thái của data model.Sẽ không có lỗi khi bạn lấy một object hoặc thuộc tính không tồn tại và khi bạn chỉ định một giá trị cho một object hoặc thuộc tính không tồn tại, AngularJS sẽ đơn giản tạo ra nó cho bạn, sinh ra một giá trị hoặc object.

Để xem được hiệu ứng nói trên hãy nhập một vài text cho input element, select một giá trị trong select element và click Add button.Các tương tác của bạn với input và select element sẽ tạo ra newTodo object và thuộc tính của nó, khi đó ng-click directive được áp dụng cho button element sẽ gọi hành vi của cotroller sử dụng các giá trị này để tạo ra new to-do item trong list, kết quả như hình sau:

Nguồn: Pro AngularJS book-Chapter 12

0