简体   繁体   中英

AngularJS Custom Directive similar to ng-requried

So I have spent about 4 hours on this already.
What I am trying to achieve is a custom directive that is similar to ng-required which has an input and does the validation.
Following is the ng-required code.

var requiredDirective = function() {
  return {
    restrict: 'A',
    require: '?ngModel',
    link: function(scope, elm, attr, ctrl) {
      if (!ctrl) return;
      attr.required = true; // force truthy in case we are on non input element

      ctrl.$validators.required = function(modelValue, viewValue) {
        return !attr.required || !ctrl.$isEmpty(viewValue);
      };

      attr.$observe('required', function() {
        ctrl.$validate();
      });
    }
  };
};

I know when the required attribute changes it's value, we will do validation again. Say if you have

<input 
ng-model="sor.seal"
ng-model-options="{ updateOn : 'default blur' }"
class="form-control" 
ng-required="sor.sealUwatType=='SEAL'" 
type="text"
placeholder="Enter a SEAL/UWAT ID"
id="seal" 
name="seal" 
/>

How does it add an attribute required="required" when the ng-required expression is true?
And How does it remove the attribute when the ng-required expression is false?

Angular creates an internal ng-attribute Alias directive on all the boolean attribute equivalents like ng-required, ng-checked, ng-disabled etc...

Source

var BOOLEAN_ATTR = {};
forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
  BOOLEAN_ATTR[lowercase(value)] = value;
});

and the generic link function registered on these performs a generic toggling of attributes

Source

forEach(BOOLEAN_ATTR, function(propName, attrName) {
  // binding to multiple is not supported
  if (propName == "multiple") return;

  function defaultLinkFn(scope, element, attr) {
    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
      attr.$set(attrName, !!value);
    });
  }

  var normalized = directiveNormalize('ng-' + attrName);
  var linkFn = defaultLinkFn;

  if (propName === 'checked') {
    linkFn = function(scope, element, attr) {
      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
      if (attr.ngModel !== attr[normalized]) {
        defaultLinkFn(scope, element, attr);
      }
    };
  }

  ngAttributeAliasDirectives[normalized] = function() {
    return {
      restrict: 'A',
      priority: 100,
      link: linkFn
    };
  };
});

So basically ng-required adds the attribute required which itself is registered internally as a directive that does the validation for ng-model .

You can verify this by outputting what ngRequired directive actually holds, it holds 2 directive configurations, one with priority 100 (which runs before ng-model), which sets/resets the attribute on the element it is upon and another one which has the validation hook with the ng-model and validators.

.config(function($provide) {
  $provide.decorator('ngRequiredDirective', function($delegate) {
    console.log($delegate); //Check the console on the configuration you will see array of 2 configurations
    return $delegate;
  })
});

This is as good as creating 2 configurations for same directive selector, example:

 angular.module('app', []).directive('myDir', function() { return { restrict: 'A', link: function() { console.log("A"); } } }).directive('myDir', function() { return { restrict: 'A', link: function() { console.log("B"); } } }).config(function($provide) { $provide.decorator('myDirDirective', function($delegate) { console.log($delegate); //Check the console on the configuration you will see array of 2 configurations return $delegate; }) }); 
 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script> <div ng-app="app"> <div my-dir></div> </div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM