在 drinkLonghand
中,你使用了以下代码
scope.flavor = attrs.flavor
在链接阶段,插值属性尚未被评估,因此它们的值为undefined
。(它们在ng-repeat
之外工作是因为在这些实例中,您不使用字符串插值; 您只是传递一个普通的字符串,例如“strawberry”)。这在指令开发人员指南中提到,以及$observe
上的一种方法,在API文档中不存在:
使用$observe
观察包含插值(例如src="{{bar}}"
)的属性的值更改。这不仅非常高效,而且也是唯一轻松获取实际值的方法,因为在链接阶段插值尚未评估,因此该值此时设置为undefined
。
因此,要解决此问题,您的drinkLonghand
指令应如下所示:
app.directive("drinkLonghand", function() {
return {
template: '<div>{{flavor}}</div>',
link: function(scope, element, attrs) {
attrs.$observe('flavor', function(flavor) {
scope.flavor = flavor;
});
}
};
});
然而,此方法的问题在于它不使用隔离作用域;因此,这行代码
scope.flavor = flavor
这段代码有可能会覆盖作用域中已存在的名为flavor
的变量。添加一个空的隔离作用域也不起作用,因为Angular会尝试基于指令的作用域插值字符串,而其中没有名为flav
的属性。(你可以通过在调用attrs.$observe
之前添加scope.flav ='test' ;
进行测试。)
当然,您可以通过以下隔离作用域定义来解决这个问题:
scope: { flav: '@flavor' }
或通过创建一个非隔离子作用域
scope: true
或者不依赖于带有{{flavor}}
的template
,而是进行一些直接的DOM操作,比如
attrs.$observe('flavor', function(flavor) {
element.text(flavor);
});
但这违背了练习的目的(例如,只使用drinkShortcut
方法会更容易)。因此,为了使该指令起作用,我们将使用$interpolate
服务在指令的$parent
作用域上自行进行插值:
app.directive("drinkLonghand", function($interpolate) {
return {
scope: {},
template: '<div>{{flavor}}</div>',
link: function(scope, element, attrs) {
var fn = $interpolate(element.attr('flavor'));
scope.flavor = fn(scope.$parent);
}
};
});
当然,这仅适用于
scope.$parent.flav
的初始值;如果该值能够更改,则需要使用
使用 $watch
并重新评估插值函数
fn
的结果(我不确定如何在脑海中准确知道要
$watch
什么;您可能只需传递一个函数)。
scope: { flavor: '@' }
是一个很好的快捷方式,可以避免处理所有这些复杂性。
[更新]
回答评论中的问题:
这个快捷方式是如何在后台解决这个问题的?它是否像您所做的那样使用 $interpolate 服务,还是做了其他事情?
我不确定,所以我查看了源代码。我在
compile.js
中找到了以下内容:
forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) {
var match = definiton.match(LOCAL_REGEXP) || [],
attrName = match[2]|| scopeName,
mode = match[1],
lastValue,
parentGet, parentSet;
switch (mode) {
case '@': {
attrs.$observe(attrName, function(value) {
scope[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = parentScope;
break;
}
看起来attrs.$observe
可以在内部告诉它使用不同的作用域来基于属性观察(上面倒数第二行,在break
之前)。虽然尝试自己使用这个功能可能很诱人,但请记住,任何带有双美元符号$$
前缀的内容都应该被视为Angular私有API,并且可能会在没有警告的情况下发生更改(当使用@
模式时,您已经默认获得了此功能)。