在drinkLonghand
中,您使用代码
scope.flavor = attrs.flavor;
在链接阶段,插值属性尚未评估,因此它们的值为undefined
. (它们在 之外工作,ng-repeat
因为在这些情况下,您没有使用字符串插值;您只是传递了一个普通的普通字符串,例如“草莓”。)指令开发人员指南中提到了这一点,以及关于该方法的Attributes
方法在 API 文档中不存在,称为$observe
:
用于$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
。(您可以通过scope.flav = 'test';
在调用上方添加对. 进行测试attrs.$observe
。)
当然,您可以使用隔离范围定义来解决此问题,例如
scope: { flav: '@flavor' }
或通过创建非隔离子范围
scope: true
或者不依赖template
with{{flavor}}
而是做一些直接的 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) {
// element.attr('flavor') == '{{flav}}'
// `flav` is defined on `scope.$parent` from the ng-repeat
var fn = $interpolate(element.attr('flavor'));
scope.flavor = fn(scope.$parent);
}
};
});
当然,这只适用于 ; 的初始值scope.$parent.flav
。如果该值能够更改,则您必须使用$watch
并重新评估 interpolate 函数的结果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], // @, =, or &
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 的私有内容,并且可能会在没有警告的情况下进行更改(更不用说在使用@
模式)。