AngularJS common controller functionality - mix-in where needed or define on $rootScope?

守給你的承諾、 提交于 2020-01-03 10:39:26

问题


I am using v1.2.0 rc2 of AngularJS and want to know what is the best method to provide common functionality to multiple controllers.

I have the following validation functions that I want to use in all controllers that edit a model:

$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
    };
};

I want to keep my controllers DRY so I defined a factory as follows:

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;
            }
        };
    }]);

Initially, I just mixed the factory into the controllers that needed it as follows:

$scope.canSave = validationFactory.isFormValid;

$scope.validationClasses = validationFactory.validationClasses;

But I realised I could add them to the $rootScope in the module's run method as follows:

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

Now they are available in every controller indiscriminately and there is less wiring up to do.

The functions are used in the view templates as follows:

<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>

My questions are:

  1. should I avoid adding common functions to the $rootScope? if so, what are the pitfalls?
  2. is it better to mix-in common functionality only where necessary?
  3. is there a better way of achieving the same outcome?

Updated Solution

I opted to use custom directives instead of adding functions to the $rootScope which had a nasty smell about it.

I created custom attributes validation-class-for="<input.name>" and disabled-when-invalid so the markup looks like this:

<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>

The directives simply require a form ancestor and watch a function to determine state.

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);
                });
            }
        };
    });

Now there is no need for the validation factory either.


回答1:


How are you using this validationFactory? Is it to change the appearance of for submit buttons? If so, I'd suggest creating custom components for the buttons themselves, and have the component reference the validationFactory.




回答2:


I would not suggest to use the scope for sharing logic. That reduces reusability and has influence when you want to test it.

  • I would suggest one of the following approaches: You can use class extension: http://ejohn.org/blog/simple-javascript-inheritance/ to extract common logic.
  • You can use the messaging of angular to broadcast events which toggle the state, like 'is valid' from a validation listener.
  • You can use an interceptor to prevent backend requests based on some conditions.


来源:https://stackoverflow.com/questions/19381225/angularjs-common-controller-functionality-mix-in-where-needed-or-define-on-ro

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!