Gabin
Gabin

Reputation: 1288

ng-disabled doesn't update even if form validity is populated

I created a directive that check if an input is valid based on some criteria. In this form I have a button that is ng-disabled="form.$invalid". The problem is that, even if it seems like the valid state is populated, my button is not enabled when my custom directive change the validity state of the input.

Here is a simple example:

<div ng-app="app">
  <div ng-controller="fooController">
    <form name="fooForm">
      <input type="text" ng-model="foo" foo>
      <input type="submit" value="send" ng-disabled="fooForm.$invalid">
    </form>
  </div>
</div>

JS (CoffeeScript):

app = angular.module 'app', []

app.directive 'foo', ->
    restrict: 'A'
    require: 'ngModel'
    link: (scope, element, attrs, controller) ->
      element.bind 'keyup', ->
        if controller.$viewValue isnt 'foo'
          controller.$setValidity 'foo', false
        else
          controller.$setValidity 'foo', true

app.controller 'fooController', ($scope) ->
  $scope.foo = 'bar'

In short, this directive check if the input's value === 'foo'. If it's not it sets the validity 'foo' to false, otherwise to true.

Here is a jsfiddle (javascript) : http://jsfiddle.net/owwLwqbk/

I found a solution involving $apply: http://jsfiddle.net/owwLwqbk/1/

But I wonder if there's not an other, a better way of doing it? Isn't the state supposed to populate?

Upvotes: 0

Views: 898

Answers (2)

sylwester
sylwester

Reputation: 16498

Please see demo below

var app;

app = angular.module('app', []);

app.directive('foo', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, element, attrs, ctrl) {

      ctrl.$parsers.unshift(function(val) {
        console.log(val);
        if (val == "bar") {
          ctrl.$setValidity('foo', true);
        } else {
          ctrl.$setValidity('foo', false);

        }
      });





    }
  };
});

app.controller('fooController', function($scope) {
  $scope.foo = 'bar';
});
.ng-invalid-foo {
  outline: none;
  border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>


<div ng-app="app">
  <div ng-controller="fooController">
    [Only "bar" is valid value] <br/>
    <form name="fooForm">
      
      <input type="text" ng-model="foo" foo="">
      <input type="submit" value="send" ng-disabled="fooForm.$invalid" />
    </form>
  </div>
</div>

Upvotes: 1

Anthony Chu
Anthony Chu

Reputation: 37520

The jqLite event handler runs outside the context of Angular, that's why you needed the scope.$apply() before it would work.

Another option is to use a watch...

link: function(scope, element, attrs, controller) {
    scope.$watch(function () { 
        return controller.$viewValue;
    }, function (newValue) {
        controller.$setValidity('foo', newValue === 'foo');
    });
}

Fiddle

Upvotes: 1

Related Questions