2016-04-12 8 views
0

Ich habe einen Filter basierend auf this post gebaut. Das Problem ist, dass ich in einigen Objekten Eigenschaften habe, die mehrere Werte (ein Array von Werten) gleichzeitig haben können (so kann zum Beispiel eine Art von Wein gleichzeitig die Kategorie "Wein" und "Champagner" sein). Ich habe versucht, den Filter ohne Glück zu modifizieren. Hier ist mein html:Angular - Filtering Objekt mit Eigenschaften mit mehreren Werten

<div ng-controller="myCtrl"> 
<div ng-repeat="(prop, ignoredValue) in wines[0]" ng-init="filter[prop]={}"> 
    <b>{{prop}}:</b><br /> 
    <span class="quarter" ng-repeat="opt in getOptionsFor(prop)"> 
     <b><input type="checkbox" ng-model="filter[prop][opt]" />&nbsp;{{opt}}</b> 
    </span> 
    <hr /> 
</div> 
<div ng-repeat="w in filtered=(wines | filter:filterByProperties)"> 
    {{w.name}} ({{w.category}}) 
</div> 

Und mein Winkel Logik:

var app = angular.module('myApp', []); 
app.controller('myCtrl', function ($scope) { 
$scope.wines = [ 
    { name: "Wine A", category: ["red"] }, 
    { name: "Wine B", category: ["red"] }, 
    { name: "wine C", category: ["white"] }, 
    { name: "Wine D", category: ["red"] }, 
    { name: "Wine E", category: ["red"] }, 
    { name: "wine F", category: ["champagne","white"] }, 
    { name: "wine G", category: ["champagne"]}, 
    { name: "wine H", category: ["champagne"] }  
]; 
$scope.filter = {}; 

$scope.getOptionsFor = function (propName) { 
    return ($scope.wines || []).map(function (w) { 
     return w[propName]; 
    }).filter(function (w, idx, arr) { 
     return arr.indexOf(w) === idx; 
    }); 
}; 

$scope.filterByProperties = function (wine) { 
    // Use this snippet for matching with AND 
    var matchesAND = true; 
    for (var prop in $scope.filter) { 
     if (noSubFilter($scope.filter[prop])) continue; 
     if (!$scope.filter[prop][wine[prop]]) { 
      matchesAND = false; 
      break; 
     } 
    } 
    return matchesAND; 
}; 

function noSubFilter(subFilterObj) { 
    for (var key in subFilterObj) { 
     if (subFilterObj[key]) return false; 
    } 
    return true; 
} 
}); 

Ich mag es so funktioniert, wenn ein Objekt eine Eigenschaft mit einem Array hat, dass mehr als ein Wert (wie im Beispiel Weiß und Champagner), würde das Objekt ausgewählt werden, indem nur eines von ihnen (oder beide) ausgewählt wird. Danke im Voraus.

Ich habe die JsFiddle aktualisiert here

Antwort

1

Ich denke, Sie für jede Eigenschaft Objekte zu filtern versuchen. Wenn dies das ist, was Sie wollen, habe ich Ihren Code geändert, der alle Eigenschaften und alle möglichen Werte für jede Eigenschaft als Ankreuzfelder auflistet.

Zuerst gibt es jeden Wein weiter, um alle verfügbaren Filteroptionen zu erhalten und als Arrayvariable einzustellen. Entfernt auch Duplikate.

Wenn dann jedes Kontrollkästchen aktiviert ist, findet filterByCurrentFilter-Methode alle ausgewählten Filter und überprüft, ob ein Wein alle diese Kriterien unterstützt.

Hier ist ein demo.

var app = angular.module('myApp', []); 
app.controller('myCtrl', function ($scope) { 
    $scope.wines = [ 
     { name: "Wine A", category: "red" }, 
     { name: "Wine B", category: "red" }, 
     { name: "wine C", category: "white" }, 
     { name: "Wine D", category: "red" }, 
     { name: "Wine E", category: "red" }, 
     { name: "wine F", category: ["champagne","white"] }, 
     { name: "wine G", category: "champagne"}, 
     { name: "wine H", category: "champagne" }  
    ]; 
    $scope.filter = {}; 

    $scope.getFilterOptions = function() { 
      var optsAsObj = $scope.wines.reduce(function(prev,current) { 
      for (field in current) { 
       prev[field] = prev[field] || []; 
       prev[field] = prev[field].concat(current[field]) 
       // remove duplicates 
       prev[field] = prev[field].filter(function(item, pos) { 
        return prev[field].indexOf(item) == pos; 
       }); 
      } 
      return prev; 
     }, {}); 

     var result = [] 
     for (key in optsAsObj) { 
      if (key == '$$hashKey') 
       continue; 
       result.push ({ name:key, values: optsAsObj[key] }) 
     } 

     return result; 

    }; 

    // for performance, calculate once and set it to a variable 
    $scope.filterOptions = $scope.getFilterOptions(); 

    $scope.filterByCurrentFilter = function (wine) { 
     var selectedFilters = {} 
     for (filterName in $scope.filter) { 

       // i.e filterName: name, category 
       var criteria = $scope.filter[filterName] 
      console.log('filterName:' + filterName) 
      // i.e criteria: { 'Wine A': true, 'Wine B': null, 'Wine C':false } 
      for (criteriaName in criteria) { 
        // i.e. criteriaName: 'Wine A' 
        if (criteria[criteriaName]) { 

         // a filter is true for criteriaName 
         var criteriaVals = selectedFilters[filterName] || [] 
        criteriaVals.push(criteriaName); 

        console.log('filterName:' + filterName + ' criteriaName:' + criteriaName + " criteriaVals:" + criteriaVals); 

        if (criteriaVals.length > 0) 
         selectedFilters[filterName] = criteriaVals 
       } 
      } 
     } 

     for (key in selectedFilters) { 
      console.log(key + ':' + selectedFilters[key]) 
       var filterVals = selectedFilters[key]; 
      if (!filterVals || filterVals.length == 0) 
        continue; 

      var wineVal = wine[key]; 
      wineVal = angular.isArray(wineVal) ? wineVal : [wineVal]; 

      var matches = wineVal.some (function(item) { 
        return filterVals.indexOf(item) != -1; 
      }); 

      if (!matches) 
       return false; 
     } 

     return true; 
    }; 

    function noFilter(filterObj) { 
     for (var key in filterObj) { 
      if (filterObj[key]) { 
       return false; 
      } 
     } 
     return true; 
    }    
}); 
+0

Ok, ich sehe die Logik. Zusätzlich zu der anderen Lösung, in der Sie bereits nach einer Eigenschaft filtern (z. B. Name) und nach einem Wert in einer anderen Eigenschaft (z. B. Kategorie) filtern, werden nicht beide Mengen hinzugefügt. aber es macht die Kreuzung. Ich denke, das löst das Problem besser, also werde ich das auf gültig setzen. Vielen Dank! – Joe82

0

Ich würde empfehlen, alle Kategorien Umwandlung wie folgt auf Arrays:

$scope.wines = [ 
    { name: "Wine A", category: ["red"] }, 
    { name: "Wine B", category: ["red"] }, 
    { name: "wine C", category: ["white"] }, 
    { name: "Wine D", category: ["red"] }, 
    { name: "Wine E", category: ["red"] }, 
    { name: "wine F", category: ["champagne","white"] }, 
    { name: "wine G", category: ["champagne"]}, 
    { name: "wine H", category: ["champagne"] }  
]; 

Dann Sie diese Eigenschaft wie ein Array überall behandeln kann. Es wird konsistenter und lesbarer sein.

+0

Ja, Sie sind völlig richtig. Bearbeitet. – Joe82

1

wird diese Arbeit:

var app = angular.module('myApp', []); 
app.controller('myCtrl', function ($scope) { 
    $scope.wines = [ 
     { name: "Wine A", category: "red" }, 
     { name: "Wine B", category: "red" }, 
     { name: "wine C", category: "white" }, 
     { name: "Wine D", category: "red" }, 
     { name: "Wine E", category: "red" }, 
     { name: "wine F", category: ["champagne","white"] }, 
     { name: "wine G", category: "champagne"}, 
     { name: "wine H", category: "champagne" }  
    ]; 
    $scope.filter = {}; 

    $scope.getOptionsFor = function (propName) { 
     return ($scope.wines || []).map(function (w) { 
     if(w[propName] instanceof Array) 
     { 
      for (var key in w[propName]) { 
      return w[propName][key]; 
      } 
     }else{ 
      return w[propName]; 
      } 
     }).filter(function (w, idx, arr) { 
      return arr.indexOf(w) === idx; 
     }); 
    }; 

    $scope.filterByProperties = function (wine) { 
     // Use this snippet for matching with AND 
     var matchesAND = true; 
     for (var prop in $scope.filter) { 
      if (noSubFilter($scope.filter[prop])) continue; 
      if(wine[prop] instanceof Array) 
      { 
       for (var key in wine[prop]) { 
         if (!$scope.filter[prop][wine[prop][key]]){ 
        matchesAND = false; 
        }else{ 
        matchesAND = true; 
        break; 
        } 
       } 
      } 
      else if (!$scope.filter[prop][wine[prop]]) { 
       matchesAND = false; 
       break; 
      } 
     } 
     return matchesAND; 
    }; 

    function noSubFilter(subFilterObj) { 
     for (var key in subFilterObj) { 
      if (subFilterObj[key]) return false; 
     } 
     return true; 
    } 
}); 

http://jsfiddle.net/8d6f5fdf/4/

+0

Funktioniert perfekt. Als richtig gekennzeichnet. Vielen Dank! – Joe82

+0

Gern geschehen! – sreeramu