angularjs从$ scope.array中删除未在视图中反映的项目

时间:2013-05-18 00:35:04

标签: angularjs

我有一个简单的模型数组(来自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>

感谢您的帮助!

2 个答案:

答案 0 :(得分: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: 0调用该函数。 1无法将2整除,因此它会调用$scope.applications.splice(0, 1)。现在$scope.applications看起来像这样:

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

下一次循环,你会得到number: 2, index: 12可以自行整除,因此循环继续。然后我们到达number: 3, index: 23无法将2整除,因此循环调用$scope.applications.splice(2, 1)。但这错误地删除了错误的数字!现在数组看起来像这样:

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

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


最简单的解决方案是use 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出于浏览器兼容性原因,可以使用this polyfill,或者您可以使用像Underscore或Lo-Dash and use _.filter这样的库。 :

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

但是,在这种情况下,您根本不需要修改数组;如果你想根据一些输入在AngularJS视图中显示数组的位和部分,你应该看看Nick's answer

答案 1 :(得分: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。该复选框现在会影响一个值,该值会改变过滤器的行为,而不会改变您构建列表的实际模型。

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


修改

如果你看一下上面的答案,你会看到我首先创建一个名为myapp的角度模块来构建应用程序。拥有该模块后,您可以访问所有these functions来构建组件。其中一个是.filter,由于你在评论中的问题我已经知道了。

查找所有这些方法的允许语法可能很困难,但是我要关注的是我想称之为 only 的方法,我已经给出了上面的例子。采用函数参数的函数的任何参数实际上都可以由用于dependency injection的数组替换。如果你搜索Inline Annotation的那个页面,你会发现允许使用的3种格式,我认为你应该使用的唯一格式就是最后一种,这就是我刚才谈到的数组。所以过滤器的签名如下所示:

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

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

我更喜欢使用这种语法,因为它使依赖关系保持接近函数,它将服务(在这种情况下为过滤器)作为其所属模块的一部分,并且从js minifiers安全,因为依赖关系被称为字符串

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

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

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

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

这有点复杂,很大程度上是由于过滤器命名不佳,但这只是包装现有的过滤器过滤器,完全按照我们在前面的代码示例中所做的那样做。 如果您打算在不同的范围内多次使用自定义过滤器,我建议您编写自定义过滤器,否则原始功能过滤器仍然是最好的方法。