6

私は AngularJS の v1.2.0 rc2 を使用しており、複数のコントローラーに共通の機能を提供するための最良の方法を知りたいです。

モデルを編集するすべてのコントローラーで使用したい次の検証関数があります。

$scope.canSave = function (formController) {
    return formController.$dirty && formController.$valid;
};

$scope.validationClasses = function (modelController) {
    return {
        'has-error': modelController.$invalid && modelController.$dirty,
        'has-success': modelController.$valid && modelController.$dirty
    };
};

コントローラを DRY に保ちたいので、次のようにファクトリを定義しました。

angular.module('myModule', [])
    .factory('validationFactory', [function () {
        return {
            validationClasses: function (modelController) {
                return {
                    'has-error': modelController.$invalid && modelController.$dirty,
                    'has-success': modelController.$valid && modelController.$dirty
                };
            },
            isFormValid: function (formController) {
                return formController.$dirty && formController.$valid;
            }
        };
    }]);

最初は、次のように、ファクトリを必要なコントローラーにミックスしました。

$scope.canSave = validationFactory.isFormValid;

$scope.validationClasses = validationFactory.validationClasses;

しかし、次のようにモジュールの run メソッドで $rootScope に追加できることに気付きました。

angular.module('myModule', [])
    .run([
        '$rootScope',
        'validationFactory',
        function ($rootScope, validationFactory) {
            $rootScope.canSave = $rootScope.canUpdate = validationFactory.isFormValid;
            $rootScope.validationClasses = validationFactory.validationClasses;
        }]);

現在、それらはすべてのコントローラーで無差別に利用でき、配線を行う必要が少なくなります。

関数はビュー テンプレートで次のように使用されます。

<form name="questionForm" novalidate>
    <div class="form-group" ng-class="validationClasses(questionForm.question)">
        <label for="questionText" class="control-label">Text</label>
        <input type="text" ng-model="question.text" name="question"
               id="questionText" class="form-control" required/>
        <span ng-show="questionForm.question.$error.required"
              class="help-block">Question text is required</span>
    </div>
    ...
    <div class="form-group" ng-switch on="action">
        <button type="button" ng-switch-when="New" ng-click="save()" 
                ng-disabled="!canSave(questionForm)" 
                class="btn btn-primary">Save</button>
        <button type="button" ng-switch-default ng-click="update()" 
                ng-disabled="!canUpdate(questionForm)" 
                class="btn btn-primary">Update</button>
        <button type="button" ng-click="cancel()"
                class="btn btn-default">Cancel</button>
    </div>
</form>

私の質問は次のとおりです。

  1. $rootScope に一般的な関数を追加するのは避けるべきですか? もしそうなら、落とし穴は何ですか?
  2. 必要な場合にのみ共通の機能を混在させる方がよいでしょうか?
  3. 同じ結果を達成するためのより良い方法はありますか?

更新されたソリューション

悪臭がする $rootScope に関数を追加する代わりに、カスタム ディレクティブを使用することにしました。

カスタム属性を作成validation-class-for="<input.name>"disabled-when-invalidたので、マークアップは次のようになります。

<div class="form-group" validation-class-for="question">
    <label for="questionText" class="control-label">Text</label>
    <input type="text" ng-model="question.text" name="question"
           id="questionText" class="form-control" required/>
    <span ng-show="questionForm.question.$error.required"
          class="help-block">Question text is required</span>
</div>

<button type="button" ng-click="save()" disabled-when-invalid 
    class="btn btn-primary">Save</button>

ディレクティブは単にフォームの祖先を必要とし、関数を監視して状態を判断します。

angular.module('myModule', [])
    .directive('validationClassFor', function () {
        return {
            require: '^form',
            link: function (scope, element, attributes, formController) {
                scope.$watch(function () {
                    var field = formController[attributes.validationClassFor];
                    if (field.$invalid && field.$dirty) {
                        element.removeClass('has-success').addClass('has-error');
                    } else if (field.$valid && field.$dirty) {
                        element.removeClass('has-error').addClass('has-success');
                    } else {
                        element.removeClass('has-error').removeClass('has-success');
                    }
                });
            }
        };
    })
    .directive('disabledWhenInvalid', function () {
        return {
            require: '^form',
            link: function (scope, element, attributes, formController) {
                scope.$watch(function () {
                    return formController.$dirty && formController.$valid;
                }, function (value) {
                    element.prop('disabled', !value);
                });
            }
        };
    });

これで、検証ファクトリーも必要なくなりました。

4

2 に答える 2

2

ロジックを共有するためにスコープを使用することはお勧めしません。これは再利用性を低下させ、テストしたいときに影響を与えます。

  • 次のアプローチのいずれかをお勧めします: クラス拡張を使用できます: http://ejohn.org/blog/simple-javascript-inheritance/共通ロジックを抽出します。
  • angular のメッセージングを使用して、検証リスナーからの「有効です」など、状態を切り替えるイベントをブロードキャストできます。
  • インターセプターを使用して、いくつかの条件に基づいてバックエンド リクエストを防ぐことができます。
于 2013-10-15T12:36:25.107 に答える
1

この validationFactory をどのように使用していますか? 送信ボタンの外観を変更することですか? その場合は、ボタン自体のカスタム コンポーネントを作成し、そのコンポーネントに validationFactory を参照させることをお勧めします。

于 2013-10-15T12:43:24.737 に答える