问题
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:
- should I avoid adding common functions to the $rootScope? if so, what are the pitfalls?
- is it better to mix-in common functionality only where necessary?
- 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