Angular.js源码分析之ngcontroller
在angular中用的最多的指令可能就是ng-controller
,下边就一起来分析下这个指令。
先来看下源码:
var ngControllerDirective = [function() {
return {
restrict: 'A',
scope: true,
controller: '@',
priority: 500
};
}];
可以看到代码是很简单的,这里可能唯一需要注意的细节就是controller: '@'
这个是神马意思?其实这个逻辑在之前分析link中分析nodeLinkFn
的时候其中有setupControllers
这个函数,他有这样的逻辑:
// 在controller中可以注入的东西
var locals = {
$scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
$element: $element,
$attrs: attrs,
$transclude: transcludeFn
};
// 省略
var controller = directive.controller;
if (controller == '@') {
// ngController设置的controller的值是@
// 需要找回属性中的该值
controller = attrs[directive.name];
}
// 调用$controller服务 但是 是延迟的
var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
所以上边的指令中controller
的意思就很明显了,就是取得其属性(ng-controller
)值。但是后边的一行逻辑值得我们注意,那就是$controller
服务。
$controller
先看代码:
var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
/**
* controller Provider
* 用来创建新的controller
*/
function $ControllerProvider() {
var controllers = {},
globals = false;
// 注册controller 其实Module实例的controller方法就是利用这个注册的
this.register = function(name, constructor) {
assertNotHasOwnProperty(name, 'controller');
if (isObject(name)) {
extend(controllers, name);
} else {
controllers[name] = constructor;
}
};
// 设置允许从全局中查找controller定义
this.allowGlobals = function() {
globals = true;
};
this.$get = ['$injector', '$window', function($injector, $window) {
// provider实例
return function(expression, locals, later, ident) {
// 私有API:
// param `later` --- 如果是later的话 也就意味着不是立即调用$injector.instantiate得到实例
// 而是加一个闭包 返回一个函数 当这个函数再次被调用的时候才返回真正的实例
// 主要用在$compile中绑定独立scope的时候
// param `ident` --- 其实就是controller as ident的东东
var instance, match, constructor, identifier;
later = later === true;
if (ident && isString(ident)) {
identifier = ident;
}
if (isString(expression)) {
// 如果是字符串 则取得在controllers中对应key的定义值
match = expression.match(CNTRL_REG);
if (!match) {
throw $controllerMinErr('ctrlfmt',
"Badly formed controller string '{0}'. " +
"Must match `__name__ as __id__` or `__name__`.", expression);
}
constructor = match[1],
// controller as 语法支持
identifier = identifier || match[3];
expression = controllers.hasOwnProperty(constructor)
? controllers[constructor]
: getter(locals.$scope, constructor, true) ||
(globals ? getter($window, constructor, true) : undefined);
assertArgFn(expression, constructor, true);
}
if (later) {
// later的话 就加一层闭包
// 这个函数继承controller函数的prototype
var controllerPrototype = (isArray(expression) ?
expression[expression.length - 1] : expression).prototype;
instance = Object.create(controllerPrototype || null);
if (identifier) {
addIdentifier(locals, identifier, instance, constructor || expression.name);
}
var instantiate;
return instantiate = extend(function() {
var result = $injector.invoke(expression, instance, locals, constructor);
if (result !== instance && (isObject(result) || isFunction(result))) {
instance = result;
if (identifier) {
// If result changed, re-assign controllerAs value to scope.
addIdentifier(locals, identifier, instance, constructor || expression.name);
}
}
return instance;
}, {
instance: instance,
identifier: identifier
});
}
instance = $injector.instantiate(expression, locals, constructor);
if (identifier) {
addIdentifier(locals, identifier, instance, constructor || expression.name);
}
return instance;
};
// controller as的语法
// 可以看到其实也就是在$scope中加了一下identifier的值为instance
function addIdentifier(locals, identifier, instance, name) {
if (!(locals && isObject(locals.$scope))) {
throw minErr('$controller')('noscp',
"Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
name, identifier);
}
locals.$scope[identifier] = instance;
}
}];
}
// 根据path得到obj对象上的值
// 类似于命名空间的概念
// obj = {a:{b:{}}}
// getter(obj, 'a.b')
//TODO(misko): this function needs to be removed
function getter(obj, path, bindFnToScope) {
if (!path) return obj;
var keys = path.split('.');
var key;
var lastInstance = obj;
var len = keys.length;
for (var i = 0; i < len; i++) {
key = keys[i];
if (obj) {
obj = (lastInstance = obj)[key];
}
}
if (!bindFnToScope && isFunction(obj)) {
return bind(lastInstance, obj);
}
return obj;
}
其实整体逻辑还是不复杂的,主要是调用$injector.instantiate
这个方法来实例化定义的controller
,只是这个时候传入了重要的locals
,而locals
包含什么呢?就是上边在分析controller: '@'
这个的意思的时候上边的那部分代码:
// 在controller中可以注入的东西
var locals = {
$scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
$element: $element,
$attrs: attrs,
$transclude: transcludeFn
};
结语
到这里就分析完了ng-controller
指令,其中重点是分析了$controller
这个服务。整体还好,没有复杂逻辑,还是比较容易理解的。下篇准备分析下比较特殊的一个指令ng-if
,去一探究竟。