0

I'd like to enforce unique usernames in my Firebase app and immediately let the user know if the inserted username is already taken or not. I looked into AngularJS's ngModel since it has a built-in asyncValidator in its controller (example in Custom Validation), but I'm still stuck in get it work.

html

<form name="settings" novalidate>
  <div>
    Username:
    <input type="text" ng-model="user.username" name="username" app-username><br>
    <span ng-show="settings.username.$pending.appUsername">Checking if this username is available...</span>
    <span ng-show="settings.username.$error.appUsername">This username is already taken!</span>
  </div>
</form>

directive

app.directive('appUsername', function() {
  return {
    require: 'ngModel',
    link: function (scope, elm, attrs, ctrl) {
      var ref = new Firebase("https://<MY-APP>.firebaseio.com");
      ctrl.$asyncValidators.appUsername = function (modelValue, viewValue) {
        if (ctrl.$isEmpty(modelValue)) {
          // consider empty model valid
          return true;
        }
        var q = ref.child('users').orderByChild('username').equalTo(modelValue);
        q.once('value', function (snapshot) {
          if (snapshot.val() === null) {
            // username does not yet exist, go ahead and add new user
            return true;
          } else {
            // username already exists, ask user for a different name
            return false;
          }
        });
      };
    };
  };
});

Is it possible to use Firebase in an asyncValidator? Thanks in advance.

Community
  • 1
  • 1
xAlien95
  • 346
  • 4
  • 16

1 Answers1

0

Yes, it's possible. However, the issue you're running into isn't Firebase related. Per the Angular docs you linked:

Functions added to the object must return a promise that must be resolved when valid or rejected when invalid.

So you simply need to use a deferred as done in the Angular docs, or use $q(function(resolve, reject) {...}). I've used the latter below.

app.directive('appUsername', function($q) {
  return {
    require: 'ngModel',
    link: function (scope, elm, attrs, ctrl) {
      var ref = new Firebase("https://<MY-APP>.firebaseio.com");
      ctrl.$asyncValidators.appUsername = function (modelValue, viewValue) {
        return $q(function(resolve, reject) {
          if (ctrl.$isEmpty(modelValue)) {
            // consider empty model valid
            resolve();
          }
          var usernameRef = ref.child('users').orderByChild('username').equalTo(modelValue);
          usernameRef.once('value', function (snapshot) {
            if (snapshot.val() === null) {
              // username does not yet exist, go ahead and add new user
              resolve();
            } else {
              // username already exists, ask user for a different name
              reject();
            }
          });
        });
      };
    };
  };
});
Anid Monsur
  • 4,538
  • 1
  • 17
  • 24