1

我正在使用指令来模板对象列表。根据使用指令的位置,应过滤模板中呈现的对象列表。在一种方法中,代码如下所示:

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过滤器内部抛出异常。一个目标是使指令尽可能简单,部分原因是让这个过滤器成为一个可选组件。

我想知道的是:

  1. 为什么第一种方法有效而第二种方法无效?
  2. 哪些替代方法可以满足这些要求?

请参阅完整的 Plunker 示例

4

2 回答 2

1

您的设置中存在许多问题。建议您在指令中使用完全不同的控制器引用,因为您将数据传递给指令 from 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允许这样做是因为它的指令需要过滤器。将您的过滤器放在模板中的 ng-repeat 上,或过滤指令本身内的数据。

这是一个不会引发错误的 plunker 版本

于 2013-10-26T19:42:37.657 回答
1

目前的代码中有很多竞争条件(初始化顺序)和其他细微之处。

首先,解决您描述的错误。

promise分辨率前的初始值是多少

people | selected:true第一次被评估时,people是一个未解决的承诺。因此,angular使用 value 调用过滤器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}使用 Angularfilter 优雅地处理 Promise(和其他所有内容)。我假设您有使用自定义过滤器的理由。

其他问题

指令是scope先初始化还是控制器的scope

该属性在的隔离范围上和上people都定义:ListCtrldirective

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'
  }
});

根据您希望如何与应用程序的其余部分进行通信,您可以toggleSelectlink函数或函数中定义函数controller(如此处所示)。

关于混合过滤器和承诺

Error: 10 $digest() iterations reached. Aborting!由于指令上的=list绑定和people | selected:true返回的新列表对象,您也会遇到这些更改。我怀疑问题出在这里,因为只使用对象相等而不是angular.equals使用。但是,我不是那里的专家。

我不喜欢filters在原语以外的任何东西上使用,因为很难确保对象相等并防止此类错误。promises另外,由于这些问题,我不喜欢直接在 UI 中使用。

作为一项规则,我发现让controller过滤任务和任何其他位于 UImodelview. 因此,我会$scope在控制器中定义一个属性,该属性是在promise解决之后设置的,然后在模板中使用它。这确实使控制器有点厚。

工作示例:http ://plnkr.co/edit/8eMvGUWsxQwe4fhypBf1?p=preview

于 2013-10-26T19:19:39.507 回答