Уникальный фильтр AngularJS, затем обновите все записи

Мне поручено показать список записей с AngularJS следующим образом:

$scope.colours = [
  {id:'1', name: 'red', active: true },
  {id:'2',  name: 'blue', active: false },
  {id:'3',  name: 'green', active: true },
  {id:'4',  name: 'red', active: false }
];

и предоставление пользователю возможности показывать/скрывать их с помощью флажка по категории (или в примере цвета), под которой они находятся. Итак, я взял данные и отобразил некоторые флажки, используя уникальный фильтр Angular-UI, но когда я переключаю категорию «Красный», я хочу переключить все записи со значением «красный».

Я предполагаю, что мне нужно просмотреть записи и сделать это вручную при изменении флажка. Есть ли более угловой способ сделать это?

Также является ли хорошей практикой использование Angular Filters для фильтрации результатов в ng-repeat для больших наборов данных?

См. скрипт http://jsfiddle.net/sjmcpherso/QXk9E/.

С уважением


person sjm    schedule 20.06.2013    source источник
comment
Цвета предопределены или они зависят от данных, отправляемых с сервера?   -  person Chandermani    schedule 20.06.2013


Ответы (2)


Вы можете использовать настраиваемый фильтр, который сортирует по нескольким значениям. Это также позволит вам удалить свойство active из всех ваших данных. Давайте назовем его manyToMany и примем три параметра:

  1. массив объектов для сортировки
  2. строка, указывающая, по какому свойству этих объектов следует сортировать
  3. хэш объекта логических свойств, ключи которых соотносятся с возможными значениями свойства. Значение для любого заданного свойства будет true, если свойство сортировки должно включать результаты, соответствующие этому ключу.

Вот как будет выглядеть фильтр:

.filter('manyToMany',function(){
    return function(arrInput,strProperty,objMany){
        var arrFiltered = [];
        for(var i=0,max=arrInput.length;i<max;i++){
            if(objMany[arrInput[i][strProperty]] === true){
                arrFiltered.push(arrInput[i]);
            }
        };
        return arrFiltered;
    }
});

Затем создайте новый объект в области видимости, которая будет использоваться для objMany:

$scope.activeColors = {};

И в вашем HTML установите флажки для этого объекта на основе имен цветов из уникального повторителя:

<input type="checkbox" ng-model="activeColors[colour.name]" />{{colour.name}}

И используйте фильтр, например:

<div ng-repeat="colour in colours | manyToMany:'name':activeColors">{{colour}}</div> 

Вот скрипка

person Dan    schedule 20.06.2013
comment
Да, это работает очень хорошо, спасибо, и с небольшим количеством кода, хотя мне было бы интересно узнать, почему фильтр manyToMany запускается дважды при переключении флажков, мысли? Вот скрипт jsfiddle.net/sjmcpherso/CGeg5 - person sjm; 21.06.2013
comment
Это связано с тем, как работает цикл $digest в Angular внутри. Это несколько сложно, но каждый раз, когда есть $digest на $rootScope, он и все наблюдатели его дочерних областей проверяются. Если во время этого процесса устанавливаются новые значения (что часто бывает в случае с фильтром), он снова проверяет их, чтобы убедиться, что модель стабилизировалась. Подробнее см. эту ссылку. - person Dan; 21.06.2013
comment
sh0ber, отличный код, правда он не работает для переключателей. Почему это? - person matenji; 13.10.2014

Во-первых, ваша модель неверна. Вам нужен список элементов, которые пользователь отфильтровал по критериям — в данном случае по цвету.

В таком случае не следует устанавливать свойство active для каждого из объектов. Вместо этого вы должны сделать следующее:

Шаг 1
Определите модель как:

$scope.colors = [
    {
        id: 1,
        name: "red"
    },
    {
        id: 2,
        name: "blue"
    },
    {
        id: 3,
        name: "green"
    },
    {
        id: 4,
        name: "red"
    }
];

Обратите внимание, что я еще не добавил свойство active к каждому из объектов.

Шаг 2
Затем вы определяете другую модель, которая отслеживает активные фильтры:

$scope.activeFilters = [];

Первоначально он будет пустым, но записи будут добавляться/удаляться из него в зависимости от состояния выбора каждого из фильтров.

Шаг 3 Затем у вас есть функция, которая сообщит, активен флажок или нет.

$scope.isChecked = function (item) {
    return $scope.activeFilters.indexOf(item) !== -1;
};

Шаг 4
Затем вы определяете функцию, которая будет добавлять/удалять элементы фильтра:

$scope.toggleFilter = function (filterItem) {
    //Check if the filter item already exists in the list of active filters
    if ($scope.activeFilters.indexOf(filterItem) !== -1) {
        //Yes, the filter item already exists.
        //Since we are in toggle mode, we remove the item from the active filters
        $scope.activeFilters.splice($scope.activeFilters.indexOf(filterItem), 1);
    } else {
        //No entry does not exist.
        //Since we are in toggle mode, we add the entry to the list
        $scope.activeFilters.push(filterItem);
    }
};

Каждый раз, когда любой из флажков установлен/снят, вы вызываете эту функцию.

Шаг 5
Затем вы определяете функцию, которая устанавливает данные фильтров на основе активных фильтров:

$scope.filterData = function () {
    //This will be the model that will be used to display the final or
    //filtered data
    //Reset it each time this is called
    $scope.filteredColors = [];

    //If there are no active filters, show all the colors
    if ($scope.activeFilters.length === 0) {
        $scope.filteredColors = $scope.colors;
    } else {
        for (var i = 0; i < $scope.colors.length; i++) {
            //Check if the color name is requested in the active filter
            if ($scope.activeFilters.indexOf($scope.colors[i].name) !== -1) {
                //The color has been actively set in the filter. Display it
                $scope.filteredColors.push($scope.colors[i]);
            }
        }
    }
};

//When the controller is initialized, we need to call the above function once to
//get the colors to display. Since no filters are active initially, all colors
//will be shown
$scope.filterData();

Шаг 6
Каждый раз, когда активные фильтры меняются, вам необходимо пересчитывать отображаемые цвета. Достаточно просто посмотреть

$scope.$watch('activeFilters.length', function () {
    //Check for invalid values
    if ($scope.activeFilters === null || $scope.activeFilters === undefined) {
        //Nothing to do
        return;
    } else {
        //Re-calculate the colors to display
        $scope.filterData();
    }
});

Таким образом, каждый раз, когда фильтр устанавливается/сбрасывается, список отображаемых цветов будет пересчитываться.

Шаг 7
Теперь вам нужна модель, которая будет содержать уникальные цвета для фильтра

$scope.uniqueColors = [];

for (var j = 0; j < $scope.colors.length; j++) {
    if ($scope.uniqueColors.indexOf($scope.colors[j].name) === -1) {
        //The color does not exist. Add it
        $scope.uniqueColors.push($scope.colors[j].name);
    }
}

Последний шаг
Представление. На основе модели и функций, которые мы определили ранее, теперь вам необходимо соответствующим образом определить свое представление.

<label ng-repeat="c in uniqueColors">
    <input type="checkbox" ng-checked="isChecked(c)" ng-click="toggleFilter(c)">
    {{c}}
</label>

<div ng-repeat="f in filteredColors">
    {{f}}
</div>

РЕДАКТИРОВАТЬ: Здесь аналогичная скрипка.

person callmekatootie    schedule 20.06.2013
comment
Невероятно полный и хороший ответ. Однако простое переключение фильтра кажется немного длинным (если я неправильно понимаю вопрос). - person Chris; 16.09.2014