Jon*_*nah 60 javascript angularjs angularjs-directive
如果您希望在工作代码中看到问题,请从这里开始:http://jsbin.com/ayigub/2/edit
考虑这几乎相同的方式来编写一个简单的方法:
app.directive("drinkShortcut", function() {
return {
scope: { flavor: '@'},
template: '<div>{{flavor}}</div>'
};
});
app.directive("drinkLonghand", function() {
return {
scope: {},
template: '<div>{{flavor}}</div>',
link: function(scope, element, attrs) {
scope.flavor = attrs.flavor;
}
};
});
Run Code Online (Sandbox Code Playgroud)
当它们自己使用时,这两个指令的工作和行为相同:
<!-- This works -->
<div drink-shortcut flavor="blueberry"></div>
<hr/>
<!-- This works -->
<div drink-longhand flavor="strawberry"></div>
<hr/>
Run Code Online (Sandbox Code Playgroud)
但是,在ng-repeat中使用时,只有快捷方式版本有效:
<!-- Using the shortcut inside a repeat also works -->
<div ng-repeat="flav in ['cherry', 'grape']">
<div drink-shortcut flavor="{{flav}}"></div>
</div>
<hr/>
<!-- HOWEVER: using the longhand inside a repeat DOESN'T WORK -->
<div ng-repeat="flav in ['cherry', 'grape']">
<div drink-longhand flavor="{{flav}}"></div>
</div>
Run Code Online (Sandbox Code Playgroud)
我的问题是:
Mic*_*ley 101
在drinkLonghand
,您使用代码
scope.flavor = attrs.flavor;
Run Code Online (Sandbox Code Playgroud)
在链接阶段,尚未评估插值属性,因此它们的值是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;
});
}
};
});
Run Code Online (Sandbox Code Playgroud)
但是,问题在于它不使用隔离范围; 因此,这条线
scope.flavor = flavor;
Run Code Online (Sandbox Code Playgroud)
有可能覆盖名为范围的预先存在的变量flavor
.添加空白隔离范围也不起作用; 这是因为Angular尝试根据指令的范围插入字符串,在该范围内没有调用属性flav
.(您可以通过scope.flav = 'test';
在调用上方添加来测试attrs.$observe
.)
当然,您可以使用隔离范围定义来解决此问题
scope: { flav: '@flavor' }
Run Code Online (Sandbox Code Playgroud)
或者通过创建一个非隔离的子范围
scope: true
Run Code Online (Sandbox Code Playgroud)
或者不依赖于template
with {{flavor}}
而是做一些直接的DOM操作之类的
attrs.$observe('flavor', function(flavor) {
element.text(flavor);
});
Run Code Online (Sandbox Code Playgroud)
但这违背了练习的目的(例如,使用该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);
}
};
});
Run Code Online (Sandbox Code Playgroud)
当然,这仅适用于初始值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], // @, =, or &
lastValue,
parentGet, parentSet;
switch (mode) {
case '@': {
attrs.$observe(attrName, function(value) {
scope[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = parentScope;
break;
}
Run Code Online (Sandbox Code Playgroud)
因此,似乎attrs.$observe
可以在内部告知使用与当前范围不同的范围来基于属性观察(在最后一行的上一行,在其上方break
).虽然自己使用它可能很诱人,但请记住,带有双美元$$
前缀的任何内容都应该被视为Angular私有API的私有,并且可能会在没有警告的情况下进行更改(更不用说在使用时无论如何都可以免费获得)@
模式).
归档时间: |
|
查看次数: |
37321 次 |
最近记录: |