Angular JS中ng属性的实现细节

概述

Angular JS为很多属性实现了对应的directive。通过[ng-]开头的属性实现对应的属性与scope数据进行绑定。如:ng-disabled='enabled'就会把scope.enabled与元素的disabled属性进行绑定。改变scope.enabled的值,元素的disabled属性的值也会变化。

支持的属性

第一类总共有12个属性,如下:

属性 ng属性
selected ng-selected
checked ng-checked
disabled ng-disabled
readOnly ng-readonly
required ng-required
open ng-open
minlength ng-minlength
maxlength ng-maxlength
min ng-min
max ng-max
pattern ng-pattern
step ng-step

在这一类中,directive会去利用$scope的watcher方法监控数据模型,如果有变动,就会设置属性值。需要注意的是ng-checked和ng-module绑定了相同的值,ng-checked将会失效。

第二类总共有3个,如下:

属性 ng属性
src ng-src
srcset ng-srcset
href/xlink:href ng-href

在这一类中,directive会利用attr的$observe方法监控ng属性值的变化,然后再去设置属性值,同时还会把值写入元素的property中去。需要注意的是ng-href实现了普通元素和SVG元素的统一处理。

实现细节

第一类ng属性有两种实现方式,其实现本质上是一样的,关键代码:

forEach(BOOLEAN_ATTR, function(propName, attrName) {  
// binding to multiple is not supported  
    if (propName === 'multiple')
        return;  
    function defaultLinkFn(scope, element, attr) {   
        cope.$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    
        };  
    };
});

// aliased input attrs are evaluatedforEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
  ngAttributeAliasDirectives[ngAttr] = function() {
    return {
      priority: 100,
      link: function(scope, element, attr) {
        //special case ngPattern when a literal regular expression value
        //is used as the expression (this way we don't have to watch anything).
        if (ngAttr === 'ngPattern' && attr.ngPattern.charAt(0) === '/') {
          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
          if (match) {
            attr.$set('ngPattern', new RegExp(match[1], match[2]));
            return; 
         }
        }
        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
          attr.$set(ngAttr, value);
        });
      }
    };
  };
});

其中最为关键的是directive中的link函数里的内容:

scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
   attr.$set(ngAttr, value); 
});

在这里实现了对数据的监控,以及变化时的处理。
第二类在实现的时候和第一种大同小异,有区别的也是directive的link函数:

link: function(scope, element, attr) {
  var propName = attrName, 
  name = attrName;
  if (attrName === 'href' &&      toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
    name = 'xlinkHref';
    attr.$attr[name] = 'xlink:href';
    propName = null; 
  }
  attr.$observe(normalized, function(value) {
    if (!value) {
      if (attrName === 'href') {
        attr.$set(name, null);
      }
      return;
    }
    attr.$set(name, value); 
    // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
    // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
    // to set the property as well to achieve the desired effect.
    // we use attr[attrName] value since $set can sanitize the url.
    if (msie && propName) element.prop(propName, attr[name]);
  });

这里面实现的功能稍微多一点点,首先会针对href属性进行SVG的兼容,然后通过attr的$observe方法监控属性值的变化。在属性值发生变化时首先会设置到attr中去,然后会根据需要设置到element的property中去。

使用样例




    Test


在这个样例中实现了点击按钮禁用或启用输入框,在输入框中输入数据会改变a标签的href属性值。
从中可以看到,对于第一类ng属性可以直接写数据名称,而对于第二类ng属性则需要使用双花括号括起来。

你可能感兴趣的:(Angular JS中ng属性的实现细节)