Glen Solsberry
Glen Solsberry

Reputation: 12320

AngularJS Filter Select array

I have a select that looks like this

<select
    class="form-control"
    ng-model="vm.transaction.location_from"
    ng-options="l.name for l in vm.locations">
</select>

with vm.locations sourcing from the following JSON:

[
  {
    "id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e",
    "name": "Location 1"
  },
  {
    "id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2",
    "name": "Location 2"
  }
]

Further, I have another select that looks like:

<select
    class="form-control"
    ng-model="vm.transaction.item"
    ng-options="i.name for i in vm.items">
</select>

with vm.items sourcing from the following JSON:

[
  {
    "id": "9f582e58-45dd-4341-97a6-82fe637d769e",
    "name": "20oz Soft Drink Cup",
    "locations": [
      {
        "inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
        "location_id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e"
      },
      {
        "inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
        "location_id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2"
      }
    ],
  }
]

I want to, on change of the ng-mode="vm.transaction.item" select, have the ng-model="vm.transaction.location_from" be filtered to only show values that match from the locations array. I know I can use a | filter: { }, but I'm not sure what that filter should look like.

Upvotes: 1

Views: 271

Answers (6)

hasbi
hasbi

Reputation: 11

i think filter:{ id : item.locations[0].location_id } should do the trick. here is the jsfiddle

how do you think?

Upvotes: 0

Karim Oukara
Karim Oukara

Reputation: 2706

you can do this:

<select
    class="form-control"
    ng-model="vm.transaction.item"
    ng-change="itemCahngedFn()"
    ng-options="i.name for i in vm.items"> 
</select>

var itemChangedFn = function(){
    var filtredItems = [];
    angular.forEach(vm.locations, function(item){
       if(item.name == vm.transaction.item){
           filtredItems .push(item.location); 
       }
    });

   vm.locations= filtredItems ;
}

Upvotes: 0

user1364910
user1364910

Reputation:

I would forego angular filters and use the getterSetter option of ngModelOptions.

It could look something like this:

var selectedItem, selectedLocation; 

var items = [];
var locations = [];

vm._items = items; // Static, always allow all items to be selected.
vm.locations = function () {
  // Return differing results based on selectedItem.locations. 
};

vm._transaction = {
  location: function (v) {
    /**
     * If v is null, it is not present in the selectedItem.locations array. 
     * The extra check will ensure that we don't persist a filtered out location when 
     * selecting another item. 
     */
    return (v || v === null) ? (selectedLocation = v) : selectedLocation;
  },
  item: function (v) {
    return v ? (selectedItem = v) : selectedItem;
  }
};

Here's a plunker demonstrating the behaviour.

Not as simple/straight-forward as a filter, but I would bet (at least in the case of a piped filter) that you'd possibly see a slight performance gain going with this approach.

I do not have numbers to back up the above statement, and it usually boils down to the size of your dataset anyway. Grain of salt.

If you need it to function the other way around, you could write up a secondary filter like such:

function superFilter2 (arr) {
  // If no location is selected, we can safely return the entire set.
  if (!selectedLocation) {
    return arr;
  }

  // Grab the current location ID. 
  var id = selectedLocation.id;

  // Return the items that are present in the selected location.
  return arr.filter(function (item) {
    return item.locations.map(function (l) {
      return l.location_id;
    }).indexOf(id);
  });
}

With that and the filter in the supplied plunker, there are some similarities that could be moved into higher order functions. Eventually with some functional sauce you could probably end up with a single god function that would work both ways.

Upvotes: 0

Hope this is your expected results. Below are two options I tried ... demo | http://embed.plnkr.co/689OQztgu8F800YjBB2L/

Ref : underscorejs | angular-filter | everything-about-custom-filters-in-angular-js

// 1. filter items collection by location
angular.module('demo').filter('withLocation', function () {
return function (items, selectedLocation) {
    function isLocationInLocations (elem) { return selectedLocation && elem.location_id === selectedLocation.id; }
        function itemHasLocation (elm){ return (elm.locations && elm.locations.filter(isLocationInLocations).length > 0); }
        return items.filter(itemHasLocation);
}});

// 2. filter function to check if option can be rendered ....
vm._filters.selectableItems = function(selectedLocation) {
    return function(item) {
        var locationsHasLocation = function(elem) { return selectedLocation && elem.location_id === selectedLocation.id; }
        return (item.locations && item.locations.filter(locationsHasLocation).length > 0);
    }
}

Upvotes: 1

Fridjon Gudjohnsen
Fridjon Gudjohnsen

Reputation: 827

One way to do this is to supply a filter function to filter the locations. Something like:

vm.filterFun = function(selectedLocations) {
  return function (location) {
    var n;
    if (!selectedLocations) {
      return true;
    }
    for(n=0;n<selectedLocations.length;n += 1) {
      if (selectedLocations[n].location_id === location.id) {
        return true;
      }
    }
    return false;
  }
}

This is actually a function returning a filter function, based on the item selected.

Then in your select you apply the filter with:

<select
      class="form-control"
      ng-model="vm.transaction.location_from"
      ng-options="l as l.name for l in vm.locations | filter:vm.filterFun(vm.transaction.item.locations)">
</select>

See plunker here.

Upvotes: 0

Emir Marques
Emir Marques

Reputation: 2687

var app = angular.module("Test", []);
app.controller("Ctrl1", function($scope) {
    $scope.location_fromArr =
    [{
        "id": "9f582e58-45dd-4341-97a6-82fe637d769e",
        "name": "20oz Soft Drink Cup",
        "locations": [{
            "inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
            "location_id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e"
        },{
            "inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
            "location_id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2"
        }],
    }];

    $scope.itemArr =
    [{
        "id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e",
        "name": "Location 1"
        },{
        "id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2",
        "name": "Location 2"
    }];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="Test" ng-controller="Ctrl1">
Item
<select
    class="form-control"
    ng-model="item"
    ng-options="i.name for i in itemArr">
</select>
Location
<select
    class="form-control"
    ng-model="location_from"
    ng-options="l.name for l in location_fromArr | filter:{l.id: location_from.location_id}">
</select>
</div>

Upvotes: 0

Related Questions