Фильтрация сложного объекта внутри вложенного ng-repeat

Я хочу отфильтровать объект внутри вложенного ng-repeat.

HTML:

<div ng-controller="MyController">
<input type="text" ng-model="selectedCityId" />
<ul>
    <li ng-repeat="shop in shops">
      <p ng-repeat = "locations in shop.locations | filter:search" style="display: block">
          City id: {{ locations.city_id }}
          <span style="padding-left: 20px; display: block;" ng-repeat="detail in locations.details | filter:item">Pin code: {{detail.pin}}</span>
      </p>    
    </li>
</ul>

Контроллер:

var myApp = angular.module('myApp', []);

myApp.controller('MyController', function ($scope) {

    $scope.search = function (location) {

        if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
            return true;
        }

           if (location.city_id === parseInt($scope.selectedCityId)) {
               return true;
            }
    };

    $scope.item = function (detail) {

        if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
            return true;
        }
        if (detail.pin == parseInt($scope.selectedCityId)) {
            return true;
        }
    };

    $scope.shops =
    [
       {
          "category_id":2,
          "locations":[
             {
                "city_id":368,
                "details": [{
                    "pin": 627718,
                  "state": 'MH'
                }]
             }
          ]
       },
       {
          "name":"xxx",
          "category_id":1,
          "locations":[
             {
                "city_id":400,
                "region_id":4,
                "details": [{
                    "pin": 627009,
                  "state": 'MH'
                },{
                    "pin": 129818,
                    "state": 'QA'
                }]
             },
          ]
       },
    ];

});

Вот скрипка:

http://jsfiddle.net/suCWn/210/

Я хочу использовать множественный фильтр внутри ng-repeat.

Пример: всякий раз, когда пользователь вводит идентификатор в поле ввода. Список должен быть отфильтрован по cityID или PinCode. если пользователь вводит «129818», он должен показать результат пин-кода 129818 вместе с его родительским cityID. Аналогично, если пользователь вводит 400, список должен отфильтровать и показать результат cityID с 400 вместе с его дочерним пин-кодом.

РЕДАКТИРОВАТЬ:

Обновить код http://codepen.io/chiragshah_mb/pen/aZorMe?editors=1010pting


person Chirag    schedule 24.05.2016    source источник


Ответы (4)


Во-первых, вы не должны фильтровать местоположения с совпадающими данными. Используйте что-то вроде этого в фильтре search:

$scope.search = function (location) {
    var id = parseInt($scope.selectedCityId);
    return isNaN(id) || location.city_id === id || 
           location.details.some(function(d) { return d.pin === id });
};

Чтобы отобразить подробности при фильтрации по cityID, вам нужно найти родительский location и проверить, был ли он отфильтрован.

$scope.item = function (detail) {
    var id = parseInt($scope.selectedCityId);
    return isNaN(id) || detail.pin === id || locationMatches(detail, id);
};

function locationMatches(detail, id) {
    var location = locationByDetail(detail);
    return location && location.city_id === id;
}

function locationByDetail(detail) {
    var shops = $scope.shops;
    for(var iS = 0, eS = shops.length; iS != eS; iS++) {
      for(var iL = 0, eL = shops[iS].locations.length; iL != eL; iL++) {
        if (shops[iS].locations[iL].details.indexOf(detail) >= 0) {
          return shops[iS].locations[iL];
        }
      }
    }
}

ИЗМЕНИТЬ. Другим, более гибким решением было бы удалить все фильтры из ngRepeats и выполнить фильтрацию с помощью метода, который вы вызываете для ngChange текста поиска. Вот основная структура этого подхода.

myApp.controller('MyController', function($scope, $http) { 
  var defaultMenu = [];
  $scope.currentMenu = [];
  $scope.searchText = '';

  $http.get(/*...*/).then(function (menu) { defaultMenu = menu; } );

  $scope.onSearch = function() {
    if (!$scope.searchText) {
      $scope.currentMenu = defaultMenu  ;
    }
    else {
      // do your special filter logic here...
    }
  };
});

И шаблон:

<input type="text" ng-model="searchText" ng-change="onSearch()" />
<ul>
    <li ng-repeat="category in currentMenu">
      ...   
    </li>
</ul>
person hansmaad    schedule 24.05.2016
comment
При фильтрации по идентификатору города также должен отображаться его пин-код. - person Chirag; 24.05.2016
comment
@chiragshah Должен ли он скрывать других детей при поиске по пин-коду? - person hansmaad; 24.05.2016
comment
да. Он должен скрывать других детей - person Chirag; 24.05.2016
comment
@chiragshah Немного некрасиво, но должно работать. (по какой-то причине я больше не могу обновлять скрипку ...) - person hansmaad; 24.05.2016
comment
Кажется, работает хорошо. Единственная проблема заключается в том, что $ scope.shops обновляется (объект ответа $ http) один раз после начальной загрузки страницы, что по какой-то причине отключает фильтр элементов. Можете ли вы предоставить скрипку для того же самого? - person Chirag; 26.05.2016
comment
@chiragshah Можете ли вы предоставить скрипку? Я не могу воспроизвести такое поведение. - person hansmaad; 27.05.2016
comment
Обновите кодовое слово [codepen.io/chiragshah_mb/pen/aZorMe?editors=1010estive Он содержит фактический сложный объект. Перед началом взаимодействия с пользователем обновляется $ scope.defaultmenu ($ http request - ответ обновляет его). Теперь мне нужна трехуровневая иерархия, в которой пользователь выполняет поиск на основе категории, группы и элемента (см. Скрипт). Я не менял код. Просто изменил HTML и объект. Пожалуйста, помогите мне с этим. - person Chirag; 27.05.2016
comment
@chiragshah, если категория соответствует, вы хотите увидеть все ее группы и элементы (событие, если они не совпадают)? если группа совпадает, вы хотите увидеть все ее элементы? если элемент совпадает, вы не хотите видеть несовпадающие элементы в этой группе? Это желаемое поведение? - person hansmaad; 27.05.2016
comment
Желаемое поведение: Если КАТЕГОРИЯ совпадает: - Должны быть видны все ее группы и элементы. Если GROUP соответствует: - Все элементы, только его категории, и никакая другая группа в этой категории не должна быть видна. Если ITEM совпадает: должны быть видны только его категория, группа (только этого элемента) и сам элемент. - person Chirag; 27.05.2016
comment
@chiragshah Я думаю, вам следует реализовать логику фильтра в функции, которая обновляет все меню сразу. Это наиболее гибкий подход. Я отредактировал свой ответ. Я считаю, что вы можете добавить свою особую логику самостоятельно. - person hansmaad; 27.05.2016
comment
Давайте продолжим это обсуждение в чате. - person Chirag; 27.05.2016
comment
Согласно вашему комментарию, единственная проблема заключается в том, что $ scope.shops обновляется (объект ответа $ http) один раз после начальной загрузки страницы, что по какой-то причине отключает фильтр элементов. Я думаю, вы пытаетесь использовать второй подход. Затем при ответе на вызов ajax вам нужно снова вызвать $scope.onSearch(), чтобы обновить отфильтрованный список. - person Partha Sarathi Ghosh; 31.05.2016

Я обновил ваши фильтры. Проблема в вашем search фильтре, вы проверяете только city_id, вам следует сделать следующее:

  1. Проверьте, является ли введенный идентификатор city_id
  2. Проверить, является ли введенный идентификатор pid дочерней детали данного местоположения

Аналогичная вещь для фильтра item:

  1. Проверьте, является ли введенный идентификатор pid фильтруемой детали
  2. Проверьте, является ли введенный идентификатор city_id родительского местоположения детали, переданной в

Вот рабочий jsFiddle. Надеюсь, это поможет.

person Daniel Gruszczyk    schedule 27.05.2016

Просто изменив JSON, чтобы включить city_id для детей, чтобы вам не нужно было перебирать его, чтобы получить city_id родителя, решение очень просто:

var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope) {
    $scope.search = function (location) {
        if (!$scope.selectedCityId)
            return true;
        //if user's input is contained within a city's id
        if (location.city_id.toString().indexOf($scope.selectedCityId) > -1)
            return true;
        for (var i = 0; i < location.details.length; i++)
            //if user's input is contained within a city's pin
            if (location.details[i].pin.toString().indexOf($scope.selectedCityId) > -1)
                return true;
    };

    $scope.item = function (detail) {
        if (!$scope.selectedCityId)
            return true;
        //if user's input is contained within a city's id
        if (detail.city_id.toString().indexOf($scope.selectedCityId) > -1)
            return true;
        //if user's input is contained within a city's pin
        if (detail.pin.toString().indexOf($scope.selectedCityId) > -1)
            return true;
    };

Измененный JSON

$scope.shops=[{"category_id":2,"locations":[{"city_id":368,"details":[{"city_id":368,"pin":627718,"state":'MH'}]}]},{"name":"xxx","category_id":1,"locations":[{"city_id":400,"region_id":4,"details":[{"city_id":400,"pin":627009,"state":'MH'},{"city_id":400,"pin":129818,"state":'QA'}]},]},];});

Если прямое изменение JSON невозможно, вы можете изменить его следующим образом в этом контроллере сразу после этого оператора $scope.shops = ...json...:

for(var i=0; i<$scope.shops.length; i++)
    for(var j=0, cat=$scope.shops[i]; j<cat.locations.length; j++)
        for(var k=0, loc=cat.locations[j]; k<loc.details.length; k++)
            loc.details[k].city_id=loc.city_id;

Рабочая скрипка: http://jsfiddle.net/87e314a0/

person Thalaivar    schedule 01.06.2016
comment
codepen.io/chiragshah_mb/pen/aZorMe?editors=1010] Это это фактический вариант использования. Можете ли вы предложить решение для этого? - person Chirag; 03.06.2016

Я попытался облегчить понимание решения:

index.html:

<div ng-controller="MyController">
    <input type="text" ng-model="search.city_id" />
    <ul>
        <li ng-repeat="shop in shops">
          <p ng-repeat = "locations in shop.locations | filter:search.city_id" style="display: block">
              City id: {{ locations.city_id }}
              <span style="padding-left: 20px; display: block;" ng-repeat="detail in locations.details | filter:item">Pin code: {{detail.pin}}</span>
          </p>    
        </li>
    </ul>
</div>

app.js:

var myApp = angular.module('myApp', []);

myApp.controller('MyController', function ($scope) {
    $scope.shops =
[
   {
      "category_id":2,
      "locations":[
         {
            "city_id":368,
            "details": [{
                "pin": 627718,
              "state": 'MH'
            }]
         }
      ]
   },
   {
      "name":"xxx",
      "category_id":1,
      "locations":[
         {
            "city_id":400,
            "region_id":4,
            "details": [{
                "pin": 627009,
              "state": 'MH'
            },{
                "pin": 129818,
                            "state": 'QA'
            }]
         },
      ]
   },
];


});

Вот сценарий: mySolution

person Nouâmane Raji    schedule 02.06.2016