I would suggest writing a directive that would plug into NgModelController#$parsers pipeline (check "Custom Validation" from http://docs.angularjs.org/guide/forms).
Here is a sketch of such a directive:
.directive('uniqueEmail', ["Users", function (Users) {
return {
require:'ngModel',
restrict:'A',
link:function (scope, el, attrs, ctrl) {
//TODO: We need to check that the value is different to the original
//using push() here to run it as the last parser, after we are sure that other validators were run
ctrl.$parsers.push(function (viewValue) {
if (viewValue) {
Users.query({email:viewValue}, function (users) {
if (users.length === 0) {
ctrl.$setValidity('uniqueEmail', true);
} else {
ctrl.$setValidity('uniqueEmail', false);
}
});
return viewValue;
}
});
}
};
}])
Where the Users.query is an async call to check if an email is unique or not. Of course you should substitute this with a call to your back-end.
When done, this directive can be used like so:
<input type="email" ng-model="user.email" unique-email>
Example of this directive was taken from the angular-app that some of the AngularJS community members try to put together to illustrate common use-cases. It might be worth checking out to see how all this fits together in the complete app.