Here's a jsfiddle: http://jsfiddle.net/javajunkie314/r6oyLe26/3/
The idea is that the myValidator and myErrorMessage directives synchronize their error message via the myErrorHandler directive. Annoyingly, I can't find any way to access the reason passed to reject by the validator.
Edit: I've updated the fiddle. Now myValidator uses two asynchronous validators. I've also created a ValidatorPromise to handle updating the myErrorHandler controller. This is as I described in my comment below.
The HTML:
<form ng-controller="testCtrl">
<div my-error-handler="">
<input type="text" name="foo" ng-model="foo.text" my-validator="">
<div my-error-message=""></div>
</div>
</form>
The JavaScript:
(function () {
var app = angular.module('myApp', []);
/* Wraps a promise. Used to update the errorHandler controller when the
* validator resolves. */
app.factory('ValidatorPromise', function ($q) {
return function (errorHandler, name, promise) {
return promise.then(
// Success
function (value) {
// No error message for success.
delete errorHandler.error[name];
return value;
},
// Failure
function (value) {
// Set the error message for failure.
errorHandler.error[name] = value;
return $q.reject(value);
}
);
};
});
app.controller('testCtrl', function ($scope) {
$scope.foo = {
text: ''
};
});
app.directive('myErrorHandler', function () {
return {
controller: function () {
this.error = {};
}
};
});
app.directive('myValidator', function ($timeout, $q, ValidatorPromise) {
return {
require: ['ngModel', '^myErrorHandler'],
link: function (scope, element, attrs, controllers) {
var ngModel = controllers[0];
var myErrorHandler = controllers[1];
ngModel.$asyncValidators.test1 = function () {
return ValidatorPromise(
myErrorHandler, 'test1',
$timeout(function () {}, 1000).then(function () {
return $q.reject('Fail 1!');
})
);
};
ngModel.$asyncValidators.test2 = function () {
return ValidatorPromise(
myErrorHandler, 'test2',
$timeout(function () {}, 2000).then(function () {
return $q.reject('Fail 2!');
})
);
};
}
};
});
app.directive('myErrorMessage', function () {
return {
require: '^myErrorHandler',
link: function (scope, element, attrs, myErrorHandler) {
scope.error = myErrorHandler.error;
},
/* This template could use ngMessages to display the errors
* nicely. */
template: 'Error: {{error}}'
};
});
})();