AngularJS表單開發

表單控件(input, select, textarea )是用來獲取用戶輸入的。表單則是一組有聯繫的表單控件的集合。

用戶能通過表單和表單控件提供驗證的服務,知道自己的輸入是否合法。這樣能讓用戶交互變得友好,因爲用戶能通過反饋來修正自己的錯誤。不過,雖然客戶端的驗證能夠起到很大作用,但也很容易被繞過,所以不能完全依靠客戶端驗證。 要建立安全的應用,服務器端驗證還是必不可少的。

簡單表單

瞭解AngularJS雙向綁定的關鍵在於瞭解ngModel指令。這個指令通過動態將model和view互相映射,來實現雙向綁定。此外它還提供API給其它指令來增強它們的行爲。formatDate

index.html:

<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <div ng-controller="Controller">
      <form novalidate class="simple-form">
        Name: <input type="text" ng-model="user.name" /><br />
        E-mail: <input type="email" ng-model="user.email" /><br />
        Gender: <input type="radio" ng-model="user.gender" value="male" />male
        <input type="radio" ng-model="user.gender" value="female" />female<br />
        <button ng-click="reset()">RESET</button>
        <button ng-click="update(user)">SAVE</button>
      </form>
      <pre>form = {{user | json}}</pre>
      <pre>master = {{master | json}}</pre>
    </div>
  </body>
</html>

script.js:

function Controller($scope) {
  $scope.master= {};

  $scope.update = function(user) {
    $scope.master= angular.copy(user);
  };

  $scope.reset = function() {
    $scope.user = angular.copy($scope.master);
  };

  $scope.reset();
}

注意例子中的novalidate是用來屏蔽瀏覽器本身的驗證功能的。

使用css的類名

爲了能美化表單和表單元素,ngModel指令會自動爲元素添加以下css類:

  • ng-valid
  • ng-invalid
  • ng-pristine
  • ng-dirty

下面的例子演示瞭如何使用CSS來顯示錶單的驗證結果。其中的user.name和user.email是必填項,它們沒有被填寫就提交時,底色會變紅。這能避免用戶注意力一開始就被分散。只有在與界面交互之後才顯示錯誤。

index.html:

<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <div ng-controller="Controller">
      <form novalidate class="css-form">
        Name:
          <input type="text" ng-model="user.name" required /><br />
        E-mail: <input type="email" ng-model="user.email" required /><br />
        Gender: <input type="radio" ng-model="user.gender" value="male" />male
        <input type="radio" ng-model="user.gender" value="female" />female<br />
        <button ng-click="reset()">RESET</button>
        <button ng-click="update(user)">SAVE</button>
      </form>
    </div>

    <style type="text/css">
      .css-form input.ng-invalid.ng-dirty {
        background-color: #FA787E;
      }

      .css-form input.ng-valid.ng-dirty {
        background-color: #78FA89;
      }
    </style>
  </body>
</html>

script.js:

function Controller($scope) {
  $scope.master= {};

  $scope.update = function(user) {
    $scope.master= angular.copy(user);
  };

  $scope.reset = function() {
    $scope.user = angular.copy($scope.master);
  };

  $scope.reset();
}

與表單的狀態或者表單元素狀態綁定

一個表單就是一個FormController的實例。表單實例可以通過name屬性選擇性地公開到作用域中。同樣的,一個表單控件也是一個NgModelController的實例。表單控件也能同樣的被公開到作用域中。這意味視圖能通過綁定的基本功能獲取表單或者表單控件的狀態。

這些特點讓我們能將上面的例子改造成下面這樣:

  • 只有當表單發生改變時,重置按鈕纔會被顯示出來。
  • 只有當表單有改變並且改變的值都是合法的,保存按鈕纔會被顯示出來。
  • 能自定義user.email和user.agree的錯誤信息。

index.html:

<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <div ng-controller="Controller">
      <form name="form" class="css-form" novalidate>
        Name:
          <input type="text" ng-model="user.name" name="uName" required /><br />
        E-mail:
          <input type="email" ng-model="user.email" name="uEmail" required/><br />
        <div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
          <span ng-show="form.uEmail.$error.required">Tell us your email.</span>
          <span ng-show="form.uEmail.$error.email">This is not a valid email.</span>
        </div>

        Gender: <input type="radio" ng-model="user.gender" value="male" />male
        <input type="radio" ng-model="user.gender" value="female" />female<br />

        <input type="checkbox" ng-model="user.agree" name="userAgree" required />
        I agree: <input ng-show="user.agree" type="text" ng-model="user.agreeSign"
                  required /><br />
        <div ng-show="!user.agree || !user.agreeSign">Please agree and sign.</div>

        <button ng-click="reset()" ng-disabled="isUnchanged(user)">RESET</button>
        <button ng-click="update(user)"
                ng-disabled="form.$invalid || isUnchanged(user)">SAVE</button>
      </form>
    </div>
  </body>
</html>

script.js:

function Controller($scope) {
  $scope.master= {};

  $scope.update = function(user) {
    $scope.master= angular.copy(user);
  };

  $scope.reset = function() {
    $scope.user = angular.copy($scope.master);
  };

  $scope.isUnchanged = function(user) {
    return angular.equals(user, $scope.master);
  };

  $scope.reset();
}

自定義驗證

AngularJS實現了大部分常見的html5表單輸入元素(text, number, url, email, radio, checkbox),也實現了很多用於驗證的指令(required, pattern, minlength, maxlength, min, max)。

想要定義你自己的驗證器的話,可以通過在你自己的指令中添加一個驗證函數給ngModel的控制器來實現。要想獲得控制器的引用,需要在指令中指定依賴,像下面的例子一樣。驗證函數可以寫在兩個地方。

  • 模型到視圖的更新中- 只要綁定的模型改變了,NgModelController#$formatters數組中的函數就會被輪流調用,所以每一個函數都有機會對數據進行格式化或者通過NgModelController#$setValidity來改變表單的驗證狀態。

  • 視圖到模型的更新中- 相同的,只要用戶與表單實現了就會,NgModelController#$setViewValue就會被調用。 這次是NgModelController#$parsers數組中的函數會被依次調用,每個函數都有機會來改變值或者通過NgModelController#$setValidity來改變表單的驗證狀態。

下面的例子中,我們創建了兩個指令。

  • 第一個要驗證的是輸入是否是整數。例如,1.23就不是符合驗證的值,因爲它包含了分數部分。注意,我們是將驗證數組中的項從頭取出,而不是壓入。這是因爲我們要在輸入值被改變(格式化)前,先驗證輸入的合法性。

  • 第二個指令是一個“智能浮點(smart-float)”。它能把"1.2"或者"1,2"都轉化爲合法的浮點數"1.2"。注意,這裏我們不能使用“數字輸入類型”,因爲H支持TML5的瀏覽器不允許用戶輸入像"1,2"這樣的非法值。

index.html:

<!doctype html>
<html ng-app="form-example1">
  <head>
    <script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <div ng-controller="Controller">
      <form name="form" class="css-form" novalidate>
        <div>
          Size (integer 0 - 10):
          <input type="number" ng-model="size" name="size"
                 min="0" max="10" integer />{{size}}<br />
          <span ng-show="form.size.$error.integer">This is not valid integer!</span>
          <span ng-show="form.size.$error.min || form.size.$error.max">
            The value must be in range 0 to 10!</span>
        </div>

        <div>
          Length (float):
          <input type="text" ng-model="length" name="length" smart-float />
          {{length}}<br />
          <span ng-show="form.length.$error.float">
            This is not a valid float number!</span>
        </div>
      </form>
    </div>
  </body>
</html>

script.js:

var app = angular.module('form-example1', []);

var INTEGER_REGEXP = /^\-?\d*$/;
app.directive('integer', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function(viewValue) {
        if (INTEGER_REGEXP.test(viewValue)) {
          // it is valid
          ctrl.$setValidity('integer', true);
          return viewValue;
        } else {
          // it is invalid, return undefined (no model update)
          ctrl.$setValidity('integer', false);
          return undefined;
        }
      });
    }
  };
});

var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
app.directive('smartFloat', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function(viewValue) {
        if (FLOAT_REGEXP.test(viewValue)) {
          ctrl.$setValidity('float', true);
          return parseFloat(viewValue.replace(',', '.'));
        } else {
          ctrl.$setValidity('float', false);
          return undefined;
        }
      });
    }
  };
});

實現自定義的表單控件(用ngModel)

AngularJS已經實現了所有基本的HTML表單控件(input,select,textarea),對於大部分情況應該已經夠了。但是,你還是可以通過寫指令來實現你自己的表單控件。

要和ngModel指令協同工作實現自定義控件,並且實現雙向綁定的話,需要:

  • 實現render方法。這個方法負責在數據傳遞給NgModelController#$formatters後來渲染數據。
  • 在用戶與控件交互並且模型需要被更新時,調用$setViewValue方法。這通常是在DOM的監聽事件中完成的。(查看$compileProvider.directive來獲取更多信息)

下面的例子演示瞭如何添加一個“內容可編輯”的數據雙向綁定的元素。

index.html:

<!doctype html>
<html ng-app="form-example2">
  <head>
    <script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <div contentEditable="true" ng-model="content" title="Click to edit">Some</div>
    <pre>model = {{content}}</pre>

    <style type="text/css">
      div[contentEditable] {
        cursor: pointer;
        background-color: #D0D0D0;
      }
    </style>
  </body>
</html>

script.js:

angular.module('form-example2', []).directive('contenteditable', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      // view -> model
      elm.bind('blur', function() {
        scope.$apply(function() {
          ctrl.$setViewValue(elm.html());
        });
      });

      // model -> view
      ctrl.$render = function(value) {
        elm.html(value);
      };

      // load init value from DOM
      ctrl.$setViewValue(elm.html());
    }
  };
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章