2

我有一个简单的模型数组(来自 rest API)

[{"applicationId":"64","createTime":"2012-07-13T14:56:06.395
07:00","createdByUserId":"s.s@s.com","lastActiveTime":"2012-08-31T13:45:49.869-
07:00","lastModifiedTime":"2012-07-13T14:56:06.395-
07:00","locationId":"16","locked":"1","scope":"1","stage":"STAGE_USER","status":"in 
progress","useStatus":"N","userId":"dd.hello@gmai.com"}]

我在 html 页面中有一个复选框,选中后将仅显示状态为“已提交”的项目。所以我创建了一个手表如下:

$scope.$watch('showOnlySubmitted', function(newVal, oldVal){

            if(newVal) {            
                angular.forEach($scope.applicationsCopy, function(app, index){
                    if(app.status && app.status!=="submitted"){
                        console.log("removed element at index: " + index);
                        console.log(app.applicationId + " : "  + app.status);
                        $scope.applications.splice(index,1);
                    }
                });

            }
            else {
                $scope.applications = angular.copy($scope.applicationsCopy);
                console.log("checkbox was un clicked !!");
            }
         });

如您所见,我制作了模型的副本,以便当用户取消选中过滤器时,我可以将原始模型重新复制回来。

.factory('applicationService', function($http) {
            return {
                getApplications: function(callback) {
                    $http.get('applications', {'8080': ':8080'}).success(callback);
                }
            }
        })

$scope.applicationsCopy = angular.copy($scope.applications);

我可以在控制台中看到,如果项目状态不是“已提交”,则数组正在拼接,但拼接的项目在视图中仍然可见!。

视图绑定到 $scope.applications 如下:

<tr ng-repeat="app in applications">
    <td>{{app.applicationId}}</td>
    <td>{{app.projectName}}</td>
    <td>{{app.createTime}}</td>
    <td>{{app.status}}</td>
    <td>{{app.createdByUserId}}</td>
</tr>

谢谢您的帮助 !。

4

2 回答 2

2

这正是角度过滤器的用途。我写了一个fiddle,解释如下:

<tr ng-repeat="app in applications | filter:myFilterFunction">
    <td>{{app.applicationId}}</td>
    <td>{{app.projectName}}</td>
    <td>{{app.createTime}}</td>
    <td>{{app.status}}</td>
    <td>{{app.createdByUserId}}</td>
</tr>

重要的部分是myFilterFunction我在控制器上编写的,但您可以在任何有作用域的地方添加它。

var myapp = angular.module('myapp', []);
myapp.controller('testController', ['$scope', function($scope) {
    $scope.myFilterFunction = function (item) {
        if (!$scope.hideSubmitted) return true;
        return item.status !== "submitted";
    };
    $scope.hideSubmitted = false;
}]);

myFilterFunction将为数组中的每个元素调用 ,并为您希望可见的元素返回 true 。该复选框现在会影响一个值,该值会改变过滤器的行为,而不会改变您从中构建列表的实际模型。

Angular 过滤器的优点是不会更改您从中构建视图的模型,但会忽略您现在不想与之交互的部分。这比更改模型并将原始状态存储在另一个元素中要简单得多。最好始终保持一个规范模型并使用角度过滤器来控制实际显示的内容。


编辑

如果您查看上面的答案,您会看到我首先创建了一个名为 Angularmyapp的模块来构建应用程序。拥有模块后,您就可以访问所有这些功能来构建组件。其中之一是.filter我认为您已经知道,因为您在评论中提出了问题。

为所有这些方法找到允许的语法可能很困难,但我将重点关注的是我喜欢称之为唯一的一种,并且我已经在上面给出了一个示例。接受函数参数的函数的任何参数实际上都可以替换为用于依赖注入的数组。如果您搜索该页面,Inline Annotation您将找到允许的 3 种格式,我认为您应该使用的唯一一种是最后一种,它采用我刚才谈到的数组。所以过滤器的签名看起来像这样:

myModule.filter('name', ['dependency1', 'dependency2', function(arg1, arg2) { 
}]);

工厂函数必须是最后一个参数,并且所有依赖项都按顺序映射到 args,而不管名称如何。您可以根据需要拥有任意数量的依赖项。即使在我的服务不需要任何依赖项的情况下,如果我需要添加任何依赖项,我通常仍然使用数组语法来提醒我使用它。

我更喜欢使用这种语法,因为它使依赖项接近函数,它将服务(在这种情况下为过滤器)保留为它所属模块的一部分,并且它不受 js 压缩器的影响,因为依赖项被称为字符串。

至于将作用域注入过滤器,这是不可取的,因为工厂函数只会为过滤器调用一次。因此,最终结果是将特定过滤器绑定到它最初创建的范围,这可能并不总是正确的。事实证明,不可能将作用域注入过滤器(或任何只创建一次的服务),除非它是根作用域,它本质上对您的应用程序是全局的。您的直觉是正确的,您应该传递值。

我已经更新了小提琴以包含一个自定义过滤器:

myapp.filter('shouldShow', ['$filter', function ($filter) {
    var standardFilter = $filter('filter');
    return function (widgets, showSubmitted, showInProgress) {
        return standardFilter(widgets, function (item) {
            var shouldShow = true;
            if (item.status === "submitted") shouldShow = shouldShow && showSubmitted;
            if (item.status === "in progress") shouldShow = shouldShow && showInProgress;
            return shouldShow;
        });
    };
}]);

基本上这说注入过滤器提供程序,然后使用它来加载过滤器过滤器(不是错字,只是角度命名不佳)。然后使用现有的过滤器来实现我们的自定义过滤器。我通常发现这比每次编写自己的逻辑来循环遍历元素并手动构建结果数组要容易得多。

这有点复杂,很大程度上是由于过滤器命名不佳,但这只是包装现有的过滤器过滤器,以完全完成我们在之前的代码示例中已经完成的工作。 如果您打算在不同的范围内多次使用它,我只建议编写自定义过滤器,否则原始函数过滤器可能仍然是最好的方法。

于 2013-05-18T01:21:43.353 回答
2

想象一下您的代码如下所示:

$scope.applications     = [1, 2, 3, 4, 5, 6, 7, 8];
$scope.applicationsCopy = [1, 2, 3, 4, 5, 6, 7, 8];

现在您遍历列表,删除不能被 整除的每个项目2

angular.forEach($scope.applicationsCopy, function(number, index) {
  if (number % 2 != 0) { // not divisible by two, remove.
    $scope.applications.splice(index, 1);
  }
});

第一次通过循环,函数被调用number: 1, index: 01不能被 整除2,所以它调用$scope.applications.splice(0, 1). 现在$scope.applications看起来像这样:

[2, 3, 4, 5, 6, 7, 8];

下一次循环时,你会得到number: 2, index: 1. 2本身可以整除,所以循环继续。然后我们到number: 3, index: 2. 3不能被 整除2,所以循环调用$scope.applications.splice(2, 1). 但这错误地删除了错误的数字!现在数组看起来像这样:

[2, 3, 5, 6, 7, 8];

该数字4位于第二个数组索引中,而不是3因为该数组在循环的较早传递期间发生了变异。


最简单的解决方案是使用Array.prototype.filter

$scope.applications = $scope.applications.filter(function(number) {
  return number % 2 == 0; // only return true if the number is divisible by 2
});

Array#filter如果您出于浏览器兼容性的原因不想依赖,您可以使用这个 polyfill,或者您可以在 Underscore 或 Lo-Dash 之类的库中使用 pull并使用_.filter

$scope.applications = _.filter($scope.applications, function(number) {
  return number % 2 == 0; // only return true if the number is divisible by 2
});

但是,在这种情况下,您实际上根本不需要修改数组;如果您想根据某些输入在 AngularJS 视图中显示数组的点点滴滴,您应该查看Nick 的答案

于 2013-05-18T01:06:16.990 回答