Angular 1.x过滤器 - 修改过滤后的元素会影响原始数组

时间:2017-07-15 01:43:11

标签: javascript angularjs filter

背景信息

旅行社每天都有出发的旅行团。在这些旅行中,每个旅行团都有不同的数量 - 同一天同一个旅游出发的不同车辆同时运行。

对于列出所有已安排的游览的管理系统,我构建了一个可以做两件事的过滤器:

1)如果选择过滤天数的任何复选框(周一,周二,周三等),则只显示在这些选定日期运行的游览。

2)如果选中过滤组(第一组,第二组,第三组等)的任何复选框,则仅显示那些组。例如:如果巡视只有一个组并且选中了第二组的复选框,则该组将不会显示。如果一个巡视有三个组,并且选中了同一个第二组的复选框,那么只会显示第二个组。

问题

日间过滤器部分完美无缺。过滤器的组订单部分没有。在过滤器中,每当我从groups数组中的filteredDepartures对象中删除组时,它都会影响原始的departures数组。每当我选择第一个组顺序过滤器复选框时,除了第一个组之外的所有组都会消失,但是当我取消选中相同的复选框时,这些组不再出现,因为它们已经从原始departures数组中有效删除

这是我的过滤器代码:

app.filter('departuresFilter', function() { //Filter departures
    return function(departures, filterOptions) {

        if (typeof departures !== 'undefined') //If there are departures
        {
            var filteredDepartures = []; //Create new array

            //See if days should be filtered
            filterOptions.daysFiltered = false; //Assuming days won't be filtered
            filterOptions.days.forEach(function(day) {
                if (day.selected) //Day is selected
                    filterOptions.daysFiltered = true;
            });

            //See if group orders should be filtered
            //The array groupsInDepartures is an array that has as many elements as the highest amount of groups on any day within selected date range (typically 1-3 elements)
            filterOptions.groupOrdersFiltered = false; //Assuming group orders won't be filtered
            filterOptions.groupsInDepartures.groups.forEach(function (group) { //For every group order.
                if (group.selected) //A checkbox has been selected
                    filterOptions.groupOrdersFiltered = true;
            }); 

            for (i = 0; i < departures.length; i++) //For every tour departure
            {
                var removeDeparture = false; //Assuming departure will not be removed

                if (filterOptions.daysFiltered) //Days are filtered
                {
                    filterOptions.days.forEach(function(day) { //For every day in filter array
                        if (day.title == departures[i].date.D) //Found this group's day in day filter array
                        {
                            if (day.selected == false) //This day is not selected (should not show)
                                removeDeparture = true; //Remove day
                        }
                    });
                }

                //Departure is not to be removed - check if any groups should be removed
                if (removeDeparture == false)
                {
                    filteredDepartures.push(departures[i]); //Add departure to filtered departures array

                    if (filterOptions.groupOrdersFiltered) //Group orders should be filtered. Only show groups of which their corresponding checkbox has been selected.
                    {
                        var departureIndex = filteredDepartures.length - 1; //Get index for last departure

                        for (j = filteredDepartures[departureIndex].groups.length; j > 0; j--) //For every group in departure. Start from above, to not mess up indexes.
                        {
                            if (!filterOptions.groupsInDepartures.groups[j - 1].selected) //This group should be removed
                                filteredDepartures[departureIndex].groups.splice((j - 1), 1); //Remove group
                        }
                    }
                }
            }

            return filteredDepartures;
        }
    };
});

所以这部分就是问题所在,因为它不仅从filteredDepartures数组中删除了组,还从departures数组中删除了该组:

if (!filterOptions.groupsInDepartures.groups[j - 1].selected) //This group should be removed
    filteredDepartures[departureIndex].groups.splice((j - 1), 1); //Remove group

我还试过JSON-stingifying departures数组,然后在过滤器中创建一个全新的对象,删除对原始数组的任何引用,但是Angular给了我一个关于太多的错误消息周期。

修改

也发布HTML。第一个表用于选择日期,以及过滤日期和组(大小类型过滤尚未激活)。第二张表用于生成旅游出发列表。

<table style="margin: 40px 0;">
    <tr>
        <td>
            <h2>Dates</h2>
        </td>
        <td style="padding-left: 40px;">
            <h2>Filter groups</h2>
        </td>
        <td style="padding-left: 40px;">
            <h2>Filters applied</h2>
        </td>
    </tr>
    <tr>
        <td style="vertical-align: top;">
            <ul class="cleanList">
                <li>From <input type="text" class="form-control" ng-model="dateStart" style="width: 120px; text-align: center;" ng-change="loadGroups()" jqdatepicker></li>
                <li>To<input type="text" class="form-control" ng-model="dateEnd" style="width: 120px; text-align: center;" ng-change="loadGroups()" jqdatepicker></li>
            </ul>
        </td>
        <td style="padding-left: 40px; vertical-align: top;">
            Size Type
            <select class="form-control" ng-model="filterOptions.sizeType">
                <option></option>
                <option ng-repeat="sizeType in groupSizeTypes" value="{{ sizeType.id }}">{{ sizeType.title }}</option>
            </select>

            <ul class="horList">
                <li ng-repeat="day in filterOptions.days">
                    <div><label for="{{ day.title }}">{{ day.title }}</label></div>
                    <div style="text-align: center;"><input type="checkbox" id="{{ day.title }}" ng-model="day.selected"></div>
                </li>
            </ul>

            <div ng-show="filterOptions.groupsInDepartures.groups.length > 0">
                Groups
                <ul class="horList">
                    <li ng-repeat="group in filterOptions.groupsInDepartures.groups">
                        <div><label for="nth_group_{{ group.order }}">{{ group.order }}</label></div>
                        <div style="text-align: center;"><input type="checkbox" id="nth_group_{{ group.order }}" ng-model="group.selected"></div>
                    </li>
                </ul>
            </div>
        </td>
        <td style="padding-left: 40px; vertical-align: top;" ng-show="filterOptions.tag != '' || filterOptions.daysFiltered || filterOptions.groupOrdersFiltered">
            <ul>
                <li ng-show="filterOptions.tag != ''">Tag</li>
                <li ng-show="filterOptions.daysFiltered">Days</li>
                <li ng-show="filterOptions.groupOrdersFiltered">Groups</li>
            </ul>
        </td>
    </tr>
</table>

{{ departures }} <!-- for debugging (filtering groups from filteredDepartures removes them from this array as well) -->

<p id="loadWrap" style="display: none;"><span class="loadBox"><img src="/images/misc/ajax-loader.gif">Loading</span></p>
<p ng-show="filteredDepartures.length" class="small"><i>Showing {{ filteredDepartures.length }} departures.</i></p>

<table class="table">
    <tr>
        <th>Date</th>
        <th>Tour</th>
        <th>Size type</th>
        <th>Pax</th>
        <th>Guide</th>
        <th>Salary K CLP</th>
        <th>Vehicle</th>
        <th>Rental K CLP</th>
    </tr>
    <tbody ng-repeat="departure in filteredDepartures = (departures | departuresFilter:filterOptions)">
        <tr class="danger">
            <td><a style="cursor: pointer;" ng-click="loadThisDate(departure.date.Ymd)">{{ departure.date.Mj }}</a><div class="small" style="color: gray;">{{ departure.date.D }}</div></td>
            <td>{{ departure.tour.title }}</td>
            <td>{{ departure.tour.sizeType.title }}</td>
            <td colspan="5"></td>
        </tr>
        <tr ng-repeat="group in departure.groups" class="trNoTopBorder danger">
            <td colspan="3"></td>
            <td>{{ group.pax }} / {{ group.capacity }}</td>
            <td>{{ group.guide.name }}</td>
            <td>{{ group.salaryKCLP }}</td>
            <td>{{ group.vehicle.name }}</td>
            <td>{{ group.vehicleRentalKCLP }}</td>
        </tr>
    </tbody>
</table>

2 个答案:

答案 0 :(得分:1)

首先,避免在angularjs上使用过滤器,因为它会一次又一次地调用。尽可能使用指令,因为指令是最便宜的。

其次,如果你想要克隆一个javascript对象,你应该使用 angular.copy filteredDepartures.push(departures[i]) 上你是推送原始项目,它不会被克隆。使用filteredDepartures.push(angular.copy(departures[i]));

此外,如果filterOptions是静态的,即不是changaable,你可以只观看离开;

app.directive('departuresDirective', function () { 
    return {
        restrict: 'AC',
        link: function (scope, element, attr, ngModel) {
            var filterOptions, departures;
            scope.filteredDepartures = [];
            scope.$watchGroup([attr.filterOptions, attr.departures], function (newValues, oldValues, scope) {
                filterOptions = newValues[0];
                departures = newValues[1];
                scope.filteredDepartures =  filterDepartures(departures, filterOptions);
            }, true);

            function filterDepartures(departures, filterOptions) {
                if (typeof departures !== 'undefined') //If there are departures
                {
                    var filteredDepartures = []; //Create new array

                    //See if days should be filtered
                    filterOptions.daysFiltered = false; //Assuming days won't be filtered
                    filterOptions.days.forEach(function (day) {
                        if (day.selected) //Day is selected
                            filterOptions.daysFiltered = true;
                    });

                    //See if group orders should be filtered
                    //The array groupsInDepartures is an array that has as many elements as the highest amount of groups on any day within selected date range (typically 1-3 elements)
                    filterOptions.groupOrdersFiltered = false; //Assuming group orders won't be filtered
                    filterOptions.groupsInDepartures.groups.forEach(function (group) { //For every group order.
                        if (group.selected) //A checkbox has been selected
                            filterOptions.groupOrdersFiltered = true;
                    });

                    for (i = 0; i < departures.length; i++) //For every tour departure
                    {
                        var removeDeparture = false; //Assuming departure will not be removed

                        if (filterOptions.daysFiltered) //Days are filtered
                        {
                            filterOptions.days.forEach(function (day) { //For every day in filter array
                                if (day.title == departures[i].date.D) //Found this group's day in day filter array
                                {
                                    if (day.selected == false) //This day is not selected (should not show)
                                        removeDeparture = true; //Remove day
                                }
                            });
                        }

                        //Departure is not to be removed - check if any groups should be removed
                        if (removeDeparture == false) {
                            filteredDepartures.push(angular.copy(departures[i])); //Add departure to filtered departures array

                            if (filterOptions.groupOrdersFiltered) //Group orders should be filtered. Only show groups of which their corresponding checkbox has been selected.
                            {
                                var departureIndex = filteredDepartures.length - 1; //Get index for last departure

                                for (j = filteredDepartures[departureIndex].groups.length; j >= 0; j--) //For every group in departure. Start from above, to not mess up indexes.
                                {
                                    if (!filterOptions.groupsInDepartures.groups[j - 1].selected) //This group should be removed
                                        filteredDepartures[departureIndex].groups.splice((j - 1), 1); //Remove group
                                }
                            }
                        }
                    }

                    return filteredDepartures;
                }
            }

        }
    };
});

html for directive

    <table class="table" departures-directive="" departures="departures"  filter-options="filterOptions">
    <tr>
        <th>Date</th>
        <th>Tour</th>
        <th>Size type</th>
        <th>Pax</th>
        <th>Guide</th>
        <th>Salary K CLP</th>
        <th>Vehicle</th>
        <th>Rental K CLP</th>
    </tr>
    <tbody ng-repeat="departure in filteredDepartures track by $index">
        <tr class="danger">
            <td><a style="cursor: pointer;" ng-click="loadThisDate(departure.date.Ymd)">{{ departure.date.Mj }}</a><div class="small" style="color: gray;">{{ departure.date.D }}</div></td>
            <td>{{ departure.tour.title }}</td>
            <td>{{ departure.tour.sizeType.title }}</td>
            <td colspan="5"></td>
        </tr>
        <tr ng-repeat="group in departure.groups track by $index" class="trNoTopBorder danger">
            <td colspan="3"></td>
            <td>{{ group.pax }} / {{ group.capacity }}</td>
            <td>{{ group.guide.name }}</td>
            <td>{{ group.salaryKCLP }}</td>
            <td>{{ group.vehicle.name }}</td>
            <td>{{ group.vehicleRentalKCLP }}</td>
        </tr>
    </tbody>
</table>

编辑过滤器

app.filter('departuresFilter', function() { //Filter departures
    return function(_departures, _filterOptions) {
         var departures = angular.copy(_departures);
         var filterOptions = angular.copy(_filterOptions);
        if (typeof departures !== 'undefined') //If there are departures
        {
            var filteredDepartures = []; //Create new array

            //See if days should be filtered
            filterOptions.daysFiltered = false; //Assuming days won't be filtered
            filterOptions.days.forEach(function(day) {
                if (day.selected) //Day is selected
                    filterOptions.daysFiltered = true;
            });

            //See if group orders should be filtered
            //The array groupsInDepartures is an array that has as many elements as the highest amount of groups on any day within selected date range (typically 1-3 elements)
            filterOptions.groupOrdersFiltered = false; //Assuming group orders won't be filtered
            filterOptions.groupsInDepartures.groups.forEach(function (group) { //For every group order.
                if (group.selected) //A checkbox has been selected
                    filterOptions.groupOrdersFiltered = true;
            }); 

            for (i = 0; i < departures.length; i++) //For every tour departure
            {
                var removeDeparture = false; //Assuming departure will not be removed

                if (filterOptions.daysFiltered) //Days are filtered
                {
                    filterOptions.days.forEach(function(day) { //For every day in filter array
                        if (day.title == departures[i].date.D) //Found this group's day in day filter array
                        {
                            if (day.selected == false) //This day is not selected (should not show)
                                removeDeparture = true; //Remove day
                        }
                    });
                }

                //Departure is not to be removed - check if any groups should be removed
                if (removeDeparture == false)
                {
                    filteredDepartures.push(departures[i]); //Add departure to filtered departures array

                    if (filterOptions.groupOrdersFiltered) //Group orders should be filtered. Only show groups of which their corresponding checkbox has been selected.
                    {
                        var departureIndex = filteredDepartures.length - 1; //Get index for last departure

                        for (j = filteredDepartures[departureIndex].groups.length; j >= 0; j--) //For every group in departure. Start from above, to not mess up indexes.
                        {
                            if (!filterOptions.groupsInDepartures.groups[j - 1].selected) //This group should be removed
                                filteredDepartures[departureIndex].groups.splice((j - 1), 1); //Remove group
                        }
                    }
                }
            }

            return filteredDepartures;
        }
    };
});

答案 1 :(得分:0)

感谢Mehmet Otkun的指导,为了避免Angular的脏过滤检查,我最终编写了一个函数$scope.filterDepartures()。在每个选中框和复选框上,我添加了ng-change="filterDepartures()"。该函数的代码基本完全相同。这个版本有点长,因为我在白天已经开发了一些。但重要的细节是使用angular.copy(),以丢失对原始对象的所有引用。

这里的功能是:

$scope.filterDepartures = function() { //Filter departures
    $scope.filteredDepartures = []; //Create new array
    $scope.groupCount = 0; //Reset group count var
    $scope.filterOptions.largestGroup = 0; //Var for remembering biggest group. This is for creating capacity array for editing groups.

    //See if days should be filtered
    $scope.filterOptions.daysFiltered = false; //Assuming days won't be filtered
    $scope.filterOptions.days.forEach(function(day) {
        if (day.selected) //Day is selected
            $scope.filterOptions.daysFiltered = true;
    });

    //See if group orders should be filtered
    //The array groupsInDepartures is an array that has as many elements as the highest amount of groups on any day within selected date range (typically 1-3 elements)
    $scope.filterOptions.groupOrdersFiltered = false; //Assuming group orders won't be filtered
    $scope.filterOptions.groupsInDepartures.groups.forEach(function (group) { //For every group order.
        if (group.selected) //A checkbox has been selected
            $scope.filterOptions.groupOrdersFiltered = true;
    }); 

    for (i = 0; i < $scope.departures.length; i++) //For every tour departure
    {
        var removeDeparture = false; //Assuming departure will not be removed

        if ($scope.filterOptions.daysFiltered) //Days are filtered
        {
            $scope.filterOptions.days.forEach(function(day) { //For every day in filter array
                if (day.title == $scope.departures[i].date.D) //Found this group's day in day filter array
                {
                    if (day.selected == false) //This day is not selected (should not show)
                        removeDeparture = true; //Remove day
                }
            });
        }

        //Departure is not to be removed - check if any groups should be removed
        if (removeDeparture == false)
        {
            var tempDeparture = angular.copy($scope.departures[i]); //Create temporary departure object

            for (j = (tempDeparture.groups.length - 1); j >= 0; j--) //For every group in departure. Start from above, to not mess up indexes.
            {
                var removeGroup = false; //Assuming group shouldn't be removed

                if ($scope.filterOptions.groupOrdersFiltered && !$scope.filterOptions.groupsInDepartures.groups[j].selected) //Group orders should be filtered. Only show groups of which their corresponding checkbox has been selected.
                    removeGroup = true; //Remove group later
                else //Continue checking
                {
                    //Check if guide is filtered, and if this group has the correct guide
                    if ($scope.filterOptions.guide.exists && $scope.filterOptions.guide.id != tempDeparture.groups[j].guide.id)
                        removeGroup = true;
                    else //Guide was not filtered. Continue checking
                    {
                        //Check if vehicle is filtered, and if this group has the correct vehicle
                        if ($scope.filterOptions.vehicle.exists && $scope.filterOptions.vehicle.id != tempDeparture.groups[j].vehicle.id)
                            removeGroup = true;
                    }
                }


                if (removeGroup) //Group should be removed
                    tempDeparture.groups.splice((j), 1); //Remove group
            }

            //Loop through all groups that are left, looking for largest group
            tempDeparture.groups.forEach(function (group) {
                if (group.pax > $scope.filterOptions.largestGroup) //Found bigger group
                    $scope.filterOptions.largestGroup = group.pax; //Save to var
            });

            $scope.groupCount += tempDeparture.groups.length;

            if (!$scope.filterOptions.hideGrouplessDepartures || $scope.filterOptions.hideGrouplessDepartures && tempDeparture.groups.length > 0)
                $scope.filteredDepartures.push(tempDeparture); //Add departure to filtered departures array
        }
    }

    $scope.capEditGroupsOptions = [];
    //Renew array for editing group capacity, to let user limit capacity to the same amount of pax as the most amount of pax in any of the groups that are shown
    for (i = $scope.filterOptions.largestGroup; i <= <?php echo $maxCapacity; ?>; i++) //For every capacity option possible
    {
        $scope.capEditGroupsOptions.push(i);
    }
};

我还不确定为什么它没有使用过滤器。我唯一想到的是它是Angular中的一个错误。自Angular 1.x以来,已经有了大量的改进。使用仅在我告诉它时执行的函数(生成过滤后的数组)在任何情况下都会更好,并且肯定是我将来要做的事情。