directive指令, $CompileProvider, $compile - acelan86/angular GitHub Wiki
directive实际上是$compileProvider的directive方法,$CompileProvider依赖$provide
$CompileProvider.$inject = ['$provide'];
function $CompileProvider($provide) {
...
this.directive = function registerDirective(name, directiveFactory) {
...
};
...
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
'$controller', '$rootScope', '$document',
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
$controller, $rootScope, $document) {
return compile;
//在这里定义
function compile($compileNodes, transcludeFn, maxPriority) {
...
}
}
];
}
$CompileProvider.directive = function registerDirective(name, directiveFactory) {
var hasDirectives = {}, //记录已有指令
...
/**
* @ngdoc function
* @name ng.$compileProvider#directive
* @methodOf ng.$compileProvider
* @function
*
* @description
* Register a new directives with the compiler.
*
* @param {string} name Name of the directive in camel-case. (ie <code>ngBind</code> which will match as
* <code>ng-bind</code>).
* @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more
* info.
* @returns {ng.$compileProvider} Self for chaining.
*/
this.directive = function registerDirective(name, directiveFactory) {
if (isString(name)) {
assertArg(directiveFactory, 'directive');
if (!hasDirectives.hasOwnProperty(name)) {
hasDirectives[name] = [];
$provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
function($injector, $exceptionHandler) {
var directives = [];
forEach(hasDirectives[name], function(directiveFactory) {
try {
var directive = $injector.invoke(directiveFactory);
if (isFunction(directive)) {
directive = { compile: valueFn(directive) };
} else if (!directive.compile && directive.link) {
directive.compile = valueFn(directive.link);
}
directive.priority = directive.priority || 0;
directive.name = directive.name || name;
directive.require = directive.require || (directive.controller && directive.name);
directive.restrict = directive.restrict || 'A';
directives.push(directive);
} catch (e) {
$exceptionHandler(e);
}
});
return directives;
}]);
}
hasDirectives[name].push(directiveFactory);
} else {
forEach(name, reverseParams(registerDirective));
}
return this;
};
/**
* 查找directive
*
* @param {string} name name of the directive to look up.
* @param {string} location The directive must be found in specific format.
* String containing any of theses characters:
*
* * `E`: element name
* * `A': attribute
* * `C`: class
* * `M`: comment
* @returns true if directive was added.
* 这个方法做了下面的事情:
* 1.找到是否有这个名字的directive
* 2.找到name+'Directive'指令定义的执行数组,需要查明指令定义再那里加入到injector里面
* 3.判断指令的restrict,是否与location符合“ECMA”,这里真凑巧,js你懂的。。。, 符合,压入directives
* 4.返回是否符合指令
*/
function addDirective(tDirectives, name, location, maxPriority) {
var match = false;
//#1
if (hasDirectives.hasOwnProperty(name)) {
//#2,$injector.get就是createInjector创建时候的getService方法
for(var directive, directives = $injector.get(name + Suffix),
i = 0, ii = directives.length; i<ii; i++) {
try {
directive = directives[i];
//#3
if ( (maxPriority === undefined || maxPriority > directive.priority) &&
directive.restrict.indexOf(location) != -1) {
tDirectives.push(directive);
match = true;
}
} catch(e) { $exceptionHandler(e); }
}
}
//#4
return match;
}
//内置的一些directive的定义
var htmlAnchorDirective = valueFn({...});
var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {...}];
...//等等
//回到我们angular加载时执行的全局方法
function publishExternalAPI(angular){
...//这里是一些公共api
//创建module的定义方法
angularModule = setupModuleLoader(window);
//ngLocale
try {
angularModule('ngLocale');
} catch (e) {
angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
}
//主角来了, $compileProvieder上定义所有的Directive处理方法
angularModule('ng', ['ngLocale'], ['$provide',
function ngModule($provide) {
$provide.provider('$compile', $CompileProvider).
directive({
a: htmlAnchorDirective,
input: inputDirective,
textarea: inputDirective,
form: formDirective,
script: scriptDirective,
select: selectDirective,
style: styleDirective,
option: optionDirective,
ngBind: ngBindDirective,
ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective,
ngBindTemplate: ngBindTemplateDirective,
ngClass: ngClassDirective,
ngClassEven: ngClassEvenDirective,
ngClassOdd: ngClassOddDirective,
ngCsp: ngCspDirective,
ngCloak: ngCloakDirective,
ngController: ngControllerDirective,
ngForm: ngFormDirective,
ngHide: ngHideDirective,
ngInclude: ngIncludeDirective,
ngInit: ngInitDirective,
ngNonBindable: ngNonBindableDirective,
ngPluralize: ngPluralizeDirective,
ngRepeat: ngRepeatDirective,
ngShow: ngShowDirective,
ngSubmit: ngSubmitDirective,
ngStyle: ngStyleDirective,
ngSwitch: ngSwitchDirective,
ngSwitchWhen: ngSwitchWhenDirective,
ngSwitchDefault: ngSwitchDefaultDirective,
ngOptions: ngOptionsDirective,
ngView: ngViewDirective,
ngTransclude: ngTranscludeDirective,
ngModel: ngModelDirective,
ngList: ngListDirective,
ngChange: ngChangeDirective,
required: requiredDirective,
ngRequired: requiredDirective,
ngValue: ngValueDirective
}).
directive(ngAttributeAliasDirectives).
directive(ngEventDirectives);
...
});
}
/**
* 在给定的节点上查找指令并放入排序的directives集合里面
*
* @param node Node to search.
* @param directives An array to which the directives are added to. This array is sorted before
* the function returns.
* @param attrs The shared attrs object which is used to populate the normalized attributes.
* @param {number=} maxPriority Max directive priority.
*/
function collectDirectives(node, directives, attrs, maxPriority) {
var nodeType = node.nodeType,
attrsMap = attrs.$attr,
match,
className;
switch(nodeType) {
case 1: /* Element */
// use the node name: <directive> 节点有对应的<directive>,对应“E”类型
addDirective(directives,
directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority);
// 属性上是否有相应的directive, 对应“A”类型
for (var attr, name, nName, value, nAttrs = node.attributes,
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
attr = nAttrs[j];
if (attr.specified) {
name = attr.name;
nName = directiveNormalize(name.toLowerCase());
attrsMap[nName] = name;
attrs[nName] = value = trim((msie && name == 'href')
? decodeURIComponent(node.getAttribute(name, 2))
: attr.value);
if (getBooleanAttrName(node, nName)) {
attrs[nName] = true; // presence means true
}
addAttrInterpolateDirective(node, directives, value, nName);
addDirective(directives, nName, 'A', maxPriority);
}
}
// class上是否有相应的directive,对应“C”类型
className = node.className;
if (isString(className) && className !== '') {
while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
nName = directiveNormalize(match[2]);
if (addDirective(directives, nName, 'C', maxPriority)) {
attrs[nName] = trim(match[3]);
}
className = className.substr(match.index + match[0].length);
}
}
break;
case 3: /* Text Node */
//文本类型特殊处理,先不看吧~
addTextInterpolateDirective(directives, node.nodeValue);
break;
case 8: /* Comment */
//注释上是否有相应的directive, 对应“M”类型
try {
match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
if (match) {
nName = directiveNormalize(match[1]);
if (addDirective(directives, nName, 'M', maxPriority)) {
attrs[nName] = trim(match[2]);
}
}
} catch (e) {
// turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value.
// Just ignore it and continue. (Can't seem to reproduce in test case.)
}
break;
}
//优先级排序
directives.sort(byPriority);
return directives;
}
var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; //正则,匹配见下表,去掉一些不该有的前缀,你懂的
/**
* 下面所有方式的名称转换成'myDirective':
* my:DiRective
* my-directive
* x-my-directive
* data-my:directive
*/
function directiveNormalize(name) {
return camelCase(name.replace(PREFIX_REGEXP, ''));
}