我正在使用指令来模拟对象列表。根据指令的使用位置,应过滤模板中呈现的对象列表。在一种方法中,代码如下所示:
person_list.html
<ul>
<li ng-repeat="person in (people | selected:true)">
<a class="selected-{{ person.selected }}" ng-click="toggleSelect( person )">{{ person.name }}</a>
</li>
</ul>
person_list.js
app.directive('personList', function(){
return {
restrict: 'E',
scope: {people: '=list'},
controller: "ListCtrl",
templateUrl: 'person_list.html'
}
});
selected_filter.js
app.filter('selected', function(){
return function(list, criteria){
return list.filter(function(element){
return !!element.selected === criteria;
});
}
});
该指令的使用方法如下:
<person-list list="people"></person-list>
我想使用的另一种方法是从指令外部过滤列表:
person_list.html
<ul>
<li ng-repeat="person in people">
<a class="selected-{{ person.selected }}" ng-click="toggleSelect( person )">{{ person.name }}</a>
</li>
</ul>
该指令将按如下方式使用:
<person-list list="people | selected:true"></person-list>
但是,Angular并不喜欢这样。过滤器内部抛出异常Cannot call method 'filter' of undefined
。目标是使指令尽可能简单,部分原因是将此过滤器作为可选组件。
我想知道的是:
答案 0 :(得分:1)
目前的代码中存在相当多的竞争条件(初始化顺序)和其他细微之处。
首先,要解决你所描述的错误。
promise
的初始值首次评估people | selected:true
时,people
是未解决的承诺。因此,angular
使用值undefined
调用过滤器。这是正确的(TM)行为,因为有些过滤器会捕获undefined
,然后在后台解析值时显示一些默认值。这种情况需要在过滤器中处理:
app.filter('selected', function(){
return function(list, criteria){
if (typeof list !== 'undefined') {
return list.filter(function(element){
return !!element.selected === criteria;
});
} else {
return [];
}
}
});
使用角度people | filter:{'selected' : true}
which handles promises (and everything else) gracefully可以更轻松地将此过滤器编写为filter
。我假设您有使用自定义过滤器的原因。
scope
还是控制器scope
属性people
在ListCtrl
和directive
的隔离范围内定义:
app.directive('personList', function(){
return {
restrict: 'E',
scope: {people: '=list'}, // <-- 'people' on scope
controller: "ListCtrl", // <-- Also defines 'people' on scope
templateUrl: 'person_list.html'
}
});
目前还不清楚哪个people
会实际转移到模板上。我怀疑你需要来自控制器的toggleSelection
函数,而不是people
字段的初始化。因此,你想要这样的东西:
app.directive('personList', function(){
return {
restrict: 'E',
scope: {people: '=list'},
controller: ['$scope', function ($scope) {
$scope.toggleSelect = function (p) {
p.selected = !p.selected;
};
}],
templateUrl: 'person_list.html'
}
});
根据您希望与应用程序其余部分进行通信的方式,您可以在toggleSelect
函数或link
中定义controller
函数(如此处所示)。
通过这些更改,您将遇到Error: 10 $digest() iterations reached. Aborting!
,因为指令上的=list
绑定和返回新列表对象的people | selected:true
。我怀疑问题是here,因为只使用了对象相等而不是angular.equals
。但是,我不是那里的专家。
我不喜欢在原语以外的任何内容上使用filters
,因为很难确保对象相等并防止此类错误。另外,由于这些问题,我不是直接在UI中使用promises
的忠实粉丝。
作为一项规则,我发现最好让controller
执行过滤任务,并将任何其他逻辑放在用户界面model
和view
之间。因此,我将在控制器中的$scope
上定义一个属性,该属性在promise
解析后设置,然后在模板中使用。这确实使控制器更厚一些。
答案 1 :(得分:1)
您的设置存在许多问题。建议您在指令中使用完全不同的控制器引用,因为您将数据传递给ListCtrl
的指令,然后在指令内调用相同的控制器并再次检索相同的数据
一个问题是摘要循环将在promise中提供数据之前运行该指令。承诺似乎没有传递给指令。这意味着在首次运行过滤器时未定义people
in指令的范围
修复过滤器:
app.filter('selected', function(){
return function(list, criteria){
if(!angular.isUndefined(list)){
return list.filter(function(element){
return !!element.selected === criteria;
});
}
}
});
不要尝试过滤指令的属性。 ng-repeat
允许这样做,因为它; s指令需要过滤器。将过滤器放在模板中的ng-repeat上,或过滤指令本身内的数据。