'required' 实际上添加到 DOM 的哪里?

Where is 'required' actually added to the DOM?

我很难理解 angularjs 如何修改 DOM,特别是关于 ng-required 指令。 ng-required 指令 (from GitHub):

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();
      });
    }
  };
};

我已经使用 Chrome 的调试器在函数中放置了断点,据我所知,这段代码实际上并没有修改 DOM 中所需的值,但我可以看到 required="required" 从输入元素中添加和删除。在 angularjs 源代码中的什么地方发生了 DOM 的实际修改?

一个简单的测试表格:

        <form>
          <label>Test Select</label>
          <select ng-model="testSelect" ng-required="testIsRequired">
            <option>val 1</option>
            <option>val 2</option>
          </select>
          <label>Required
            <input type="checkbox" ng-model="testIsRequired" />
          </label>

          <label>Output</label>
          <p>
            Test is required:  {{testIsRequired}}
          </p>
          <p>
            Select Value: {{testSelect}}
          </p>
        </form>

我终于在正确的地方找到了一个断点。 AngularJS 为 'boolean' 属性('required'、'disabled' 等)设置默认的 link 函数,'multiple' 属性除外.

boolean属性被监视,attr$set方法用于改变DOM.

问题中的实际 requiredDirective 似乎只处理在 ng-required 值更改时更新验证。

相关代码(再次来自GitHub):

var ngAttributeAliasDirectives = {};

// boolean attrs are evaluated
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
    };
  };
});