3

I'm having an issue with the ng-show method in that I set the method as follows:

I check the length of the username string however even though it reports the correct length the ng-show method doesn't hide / show the extra text for me until the key stroke after. How can I get it to update the visibility of the username helper text on the key up

How if you look in the JS fiddle http://jsfiddle.net/FkAkg/8/

           accountApp.directive("stripCharacters", ['$filter', '$http', function($filter, $http) {
            return {
                restrict: 'C',
                link: function(scope, element) {
                    element.bind("keyup", function() {
                        if(scope.account.username !== undefined) {
                            element.val($filter('stripCharacters')(scope.account.username));
                            if(scope.account.username.length > 2) {
                                scope.toggleShowUsername(true); 
                                scope.usernameMessage = scope.account.usernameAvailable;
                            } else {
                                scope.toggleShowUsername(false);
                            }
                        }
                    });
                }
            }
        }]);

I've gotten it to work with replacing this with jQuery hide/show on the same element but was hoping to get it working in angular only.

Cheers

Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
Zaylril
  • 111
  • 2
  • 5
  • 3
    IMO this is the correct way of approaching this problem: http://stackoverflow.com/a/14425022/1418796 – pkozlowski.opensource Feb 20 '13 at 13:45
  • @pkozlowski.opensource is correct, plugging into the $parsers will ensure that the characters are stripped before your digest. Then you don't have to worry about scope.$apply(). – Ben Lesh Feb 20 '13 at 17:36

2 Answers2

2

The keyup handler runs "outside" of Angular, so use scope.$apply() to cause Angular to notice that you changed showUsername:

...
if(scope.account.username.length > 2) {
   scope.toggleShowUsername(true); 
   scope.usernameMessage = scope.account.usernameAvailable;
} else {
   scope.toggleShowUsername(false);
}
scope.$apply();

The above answers your question, but I recommend @pkozlowski.opensource's answer/comment.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
2

Elaborating on pkozlowski's comment...

I also think you were trying to do too much in one spot. What is that directive's (single) responsibility? According to the name it's "stripping characters"... but if you look at what you're doing inside of it, you're stripping characters, calling methods and updating display logic for elements outside of the element the directive is on.

I propose that you simplify the directive, and move the other stuff out to a $watch:

Here's a fork of your JSFiddle

And the pertinent code:

In your controller I added the following...

$scope.$watch('account.username', function (value) {
   if (value.length > 2) {
      $scope.toggleShowUsername(true);
      $scope.usernameMessage = $scope.account.usernameAvailable;
   } else {
      $scope.toggleShowUsername(false);
   }
});

Then your directive:

accountApp.directive("stripCharacters", ['$filter', function ($filter) {
   return {
      restrict: 'C',
      require: 'ngModel',
      link: function (scope, element, attrs, ngModel) {
            ngModel.$parsers.unshift(function (value) {
               var stripped = $filter('stripCharacters')(value);
               element.val(stripped);
               return stripped;
            });
      }
   }
}]);

To be clear, Mark Rajcok's answer will also work here, and may be more desirable for you... but it's missing one piece: $setViewValue. If so, you'd just change the linking function of the directive above to something like:

link: function (scope, element, attrs, ngModel) {
    element.bind('keyup', function () {
       var value = element.val();
       var stripped = $filter('stripCharacters')(value);
       element.val(stripped);
       ngModel.$setViewValue(stripped);
       scope.$apply();
    });
}

Where $setViewValue is used to actually update your model with the proper value. Here's a JSFiddle of that solution;

I hope all of that is helpful.

Ben Lesh
  • 107,825
  • 47
  • 247
  • 232