Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a custom angular directive that will override ng-disabled?

First some background: My application allows users to control whether or not fields are required, disabled, etc. through an admin tool. I have a service that takes a field name and returns me the user defined rules in a format like this:

{
  "disabled" : true,
  "required" : true
}

I want a custom attribute directive that will control these properties on an input field using the service. I would expect the usage to look something like this:

<input type="text" my-rule="fieldName" ng-model="myfield" />

I'm able to accomplish this easily with a directive like the following:

angular.module('app').directive('myRule', ['$http',
  function($http) {
    return {
      restrict: 'A',
      scope: {
        myRule: '@'
      },
      link: function(scope, element, attrs) {
        $http.get('/api/rule/'+scope.myRule).success(function(rule) {
          if (rule.disabled) {
            element.attr('disabled','disabled');
          }
          if (rule.required) {
            element.attr('required','required');
          }
        });
      }
    }
  }
]);

My problem is that if a user does not have a field disabled I may still want to disable it until, for example, another field has been filled out. This should be easy to do with ng-disabled:

<input type="text" my-rule="fieldA" ng-model="fieldA" />
<input type="text" my-rule="fieldB" ng-model="fieldB" ng-disabled="!fieldA" />

However, this does not work because if the user chooses to disable fieldB then the field should always be disabled regardless of the ng-disabled attribute but instead the ng-disabled attribute overrides the user's rule. I tried something like this to remove the ng-disabled if the field is disabled by the user but that does not seem to have an effect:

angular.module('app').directive('myRule', ['$http',
  function($http) {
    return {
      restrict: 'A',
      scope: {
        myRule: '@'
      },
      link: function(scope, element, attrs) {
        $http.get('/api/rule/'+scope.myRule).success(function(rule) {
          if (rule.disabled) {
            element.attr('disabled','disabled');
            element.removeAttr('ng-disabled');
          }
          if (rule.required) {
            element.attr('required','required');
            element.removeAttr('ng-required');
          }
        });
      }
    }
  }
]);

This removes the attribute but it seems at that point it is too late and the field still becomes enabled as soon as fieldA is filled in.

How can I dynamically remove the ng-disabled attribute in my custom directive so that it no longer has an effect on the field?

Update:

I added a code snippet demonstrating my problem.

    angular.module('app',[]).directive('myRule', ['$http',
      function($http) {
        return {
          restrict: 'A',
          scope: {
            myRule: '@'
          },
          link: function(scope, element, attrs) {
            // would normally be making an ajax call to get the rule
            var rule = { disabled: scope.myRule != "fieldA" };

            if (rule.disabled) {
              element.attr('disabled','disabled');
              element.removeAttr('ng-disabled');
            }
          }
        }
      }
    ]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="app">
  <p>Field B and Field C have been disabled by the user but since Field C includes an ng-disabled expression it will be incorrectly enabled when Field A is filled out.</p>
  <input type="text" my-rule="fieldA" ng-model="fieldA" placeholder="Field A" />
  <input type="text" my-rule="fieldB" ng-model="fieldB" placeholder="Field B" />
  <input type="text" my-rule="fieldC" ng-model="fieldC" placeholder="Field C" ng-disabled="!fieldA" />
</div>
like image 695
Kyle Avatar asked Oct 30 '22 23:10

Kyle


1 Answers

Tryprop('disabled', boolean) instead of attr(). This will change the element property which is not always the same as the attribute.

Since you are manipulating the DOM outside of angular you should probably tell angular to run a digest also by calling scope.$apply() or $timeout()

Not sure this will work and I think you will probably need a directive to wrap the whole input.

One suggestion is take a look at angular-formly which builds whole forms including conditional validation from object models

like image 156
charlietfl Avatar answered Nov 11 '22 05:11

charlietfl