src/message/valdrMessage-directive.js
/**
* This directive appends a validation message to the parent element of any input, select or textarea element, which
* is nested in a valdr-type directive and has an ng-model bound to it.
* If the form element is wrapped in an element marked with the class defined in valdrClasses.formGroup,
* the messages is appended to this element instead of the direct parent.
* To prevent adding messages to specific input fields, the attribute 'valdr-no-message' can be added to those input
* or select fields. The valdr-message directive is used to do the actual rendering of the violation messages.
*/
var valdrMessageDirectiveDefinitionFactory = function (restrict) {
return ['$compile', function ($compile) {
return {
restrict: restrict,
require: ['?^valdrType', '?^ngModel', '?^valdrFormGroup'],
link: function (scope, element, attrs, controllers) {
var valdrTypeController = controllers[0],
ngModelController = controllers[1],
valdrFormGroupController = controllers[2],
valdrNoValidate = attrs.valdrNoValidate,
valdrNoMessage = attrs.valdrNoMessage,
fieldName = attrs.name;
/**
* Don't do anything if
* - this is an <input> that's not inside of a valdr-type or valdr-form-group block
* - there is no ng-model bound to input
* - there is a 'valdr-no-validate' or 'valdr-no-message' attribute present
*/
if (!valdrTypeController || !valdrFormGroupController || !ngModelController ||
angular.isDefined(valdrNoValidate) || angular.isDefined(valdrNoMessage)) {
return;
}
var valdrMessageElement = angular.element('<span valdr-message="' + fieldName + '"></span>');
$compile(valdrMessageElement)(scope);
valdrFormGroupController.addMessageElement(ngModelController, valdrMessageElement);
scope.$on('$destroy', function () {
valdrFormGroupController.removeMessageElement(ngModelController);
});
}
};
}];
},
valdrMessageElementDirectiveDefinition = valdrMessageDirectiveDefinitionFactory('E'),
valdrMessageAttributeDirectiveDefinition = valdrMessageDirectiveDefinitionFactory('A');
var nullValdrType = {
getType: angular.noop
};
angular.module('valdr')
.directive('input', valdrMessageElementDirectiveDefinition)
.directive('select', valdrMessageElementDirectiveDefinition)
.directive('textarea', valdrMessageElementDirectiveDefinition)
.directive('enableValdrMessage', valdrMessageAttributeDirectiveDefinition)
/**
* The valdr-message directive is responsible for the rendering of violation messages. The template used for rendering
* is defined in the valdrMessage service where it can be overridden or a template URL can be configured.
*/
.directive('valdrMessage',
['$rootScope', '$injector', 'valdrMessage', 'valdrUtil', function ($rootScope, $injector, valdrMessage, valdrUtil) {
return {
replace: true,
restrict: 'A',
scope: {
formFieldName: '@valdrMessage'
},
templateUrl: function () {
return valdrMessage.templateUrl;
},
require: ['^form', '?^valdrType'],
link: function (scope, element, attrs, controllers) {
var formController = controllers[0],
valdrTypeController = controllers[1] || nullValdrType;
var updateTranslations = function () {
if (valdrMessage.translateAvailable && angular.isArray(scope.violations)) {
angular.forEach(scope.violations, function (violation) {
valdrMessage.$translate(valdrMessage.fieldNameKeyGenerator(violation))
.then(function (translation) {
violation.fieldName = translation;
})
.catch(angular.noop);
});
}
};
var createViolation = function (validatorName) {
var typeName = valdrTypeController.getType(),
fieldName = scope.formFieldName;
return {
type: typeName,
field: fieldName,
validator: validatorName,
message: valdrMessage.getMessage(typeName, fieldName, validatorName)
};
};
var addViolationsToScope = function () {
scope.violations = [];
angular.forEach(scope.formField.valdrViolations, function (violation) {
scope.violations.push(violation);
});
if (valdrMessage.angularMessagesEnabled) {
angular.forEach(scope.formField.$error, function (isValid, validatorName) {
if (!valdrUtil.startsWith(validatorName, 'valdr')) {
scope.violations.push(createViolation(validatorName));
}
});
}
scope.violation = scope.violations[0];
updateTranslations();
};
var removeViolationsFromScope = function () {
scope.violations = undefined;
scope.violation = undefined;
};
var watchFormFieldErrors = function () {
scope.formField = formController[scope.formFieldName];
if (scope.formField) {
return {
valdr: scope.formField.valdrViolations,
error: scope.formField.$error
};
}
};
scope.$watch(watchFormFieldErrors, function () {
if (scope.formField && scope.formField.$invalid) {
addViolationsToScope();
} else {
removeViolationsFromScope();
}
}, true);
var unregisterTranslateChangeHandler = $rootScope.$on('$translateChangeSuccess', function () {
updateTranslations();
});
scope.$on('$destroy', function () {
unregisterTranslateChangeHandler();
});
}
};
}]);