Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm having an issue using a dropdown that is populated with ng-repeat option values or even when using ng-options.

Basically I'm pulling a list of subsidiaries from the database. I then have a dropdown to choose a company, which in turn should populate the subsidiary dropdown with subsidiaries of the chosen company. Since many of the subsidiaries are of the same company, if I try and pull the the company name in ng-repeat, I get the same company several times. So I have created a custom filter that filters out the companyName and companyID of each company listed only once.

Everything works in the theory that when I change the value of the company dropdown, the correct subsidiaries are listed. However the value shown in the company box is stuck on the first option listed and will not change. If I remove the custom filter and allow it to list all the repeat names, the box displays correctly.

My first thought is to make a separate HTTP call that would just get companies from my companies table, but I would think I want to limit HTTP calls to as few as possible. Plus it would seem that I should be able to accomplish this.

What concept am I not grasping that prevents this from displaying correctly when I use my filter and what should I do to fix this? thanks

HTML:

<div class="col-sm-5">
   <select ng-model ="parentCompany" name="company">
      <option ng-repeat="company in companies | uniqueCompanies:'companyName'" value="{{company.id}}" >{{company.name}}</option>
    </select>                                                 
</div>

<div class="col-sm-5">
   <select name="subsidiary">
    <option ng-repeat="subsidary in companies" value="{{subsidary.subID}}" ng-hide="$parent.parentCompany !== subsidary.companyID">{{subsidary.subName}}</option>
   </select>
</div>

Controller:

  getCompanies();
            function getCompanies(){
                 $http.get("get.php?table=getcompanies").success(function(data) {
                    $scope.companies = data;
                });
            }

Filter:

.filter("uniqueCompanies", function() {
            return function(data, propertyName) {
                if (angular.isArray(data) && angular.isString(propertyName)) {
                    var results = [];
                    var keys = {};
                    for (var i = 0; i < data.length; i++) {
                        var val = data[i][propertyName];
                        var val2 = data[i]['companyID'];
                        if (angular.isUndefined(keys[val])) {
                            keys[val] = true;
                            results.push({'name':val, 'id':val2});
                        }
                    }
                    return results;
                } else {
                    return data;
                }
            };
        });

Sample Data :

[{"subID":null,"subName":null,"companyID":"1","companyName":"DWG"},
 {"subID":null,"subName":null,"companyID":"2","companyName":"Vista"},
 {"subID":"1008","subName":"Data Services","companyID":"3","companyName":"Medcare"},
 {"subID":"1009","subName":"Companion","companyID":"3","companyName":"Medcare"},
 {"subID":"1010","subName":"GBA","companyID":"3","companyName":"Medcare"},
 {"subID":"1011","subName":"PGBA","companyID":"3","companyName":"Medcare"},
 {"subID":"1013","subName":"Health Plan","companyID":"3","companyName":"Medcare"},
 {"subID":"1014","subName":"PAISC","companyID":"3","companyName":"Medcare"},
 {"subID":"1015","subName":"CGS","companyID":"3","companyName":"Medcare"}]
share|improve this question
1  
Would it help to run the filter in the controller prior to the select? –  Dylan Aug 11 '14 at 19:21
    
Because the filter is returning an array of new objects each time, you need to provide a track by expression. See if that makes a difference. –  Anthony Chu Aug 11 '14 at 19:36
    
Can you give some sample data? It's a little hard to follow because you rename properties in your filter. It looks like your data has companyName, companyID, subID and subName. What are the rules if a subsidiary has a subsidiary of it's own? i.e. "GE->NBC Universal->NBC Broadcasting"? –  Jason Goemaat Aug 11 '14 at 19:49
    
@Dylan - I have not used filters int he controller before. But on your advice i just tried adding $filter to my controller and then filtering via $scope.oldCo = $filter('uniqueCompanies')($scope.companies, 'companyName') however $scope.oldCo was coming up empty. Am I doing this incorrectly? –  Andy McCormick Aug 11 '14 at 20:01
1  
@AndyMcCormick like this works - jsfiddle.net/devitate/zfx6sbdf –  Dylan Aug 11 '14 at 23:13

2 Answers 2

up vote 1 down vote accepted

You are creating new objects in your filter with different properties so they will be different every time. You can you track by as mentioned by others. Since filters are executed every digest cycle you may want to set up a $watch and only create a new list of unique companies when your companies change. I actually get the 10 $digest() iterations reached error without doing this.

$scope.$watchCollection('companies', function(newValue) {
    $scope.filteredCompanies = $filter('uniqueCompanies')($scope.companies, 
        'companyName');
});

You could also set a watch on parentCompany and create the list of subsidiaries only when it changes, as well as clear out the value you have for subsidiaryCompany:

$scope.$watch('parentCompany', function(newValue) {
  $scope.subsidiaries = [];
  for (var i = 0; i < $scope.companies.length; i++) {
    var c = $scope.companies[i];
    if (c.companyID === newValue) {
      $scope.subsidiaries.push(c);
    }
  }
  $scope.subsidiaryCompany = undefined;
});
share|improve this answer
    
Thanks for this! I was concerned about the 10 $digest() iterations reached error, however most of the places I found talking about it online seemed to have the idea of 'just ignore it'. I tried adding the watch in the fiddle and it worked, however there seems to be another issue in my code as the fiddle that Dylan linked to works fine, however is not working in my actual app. I'll come back and mark this as best answer when I can officially say that it solves the problem. thanks –  Andy McCormick Aug 12 '14 at 13:46
    
got it! this worked great and did get rid of the iterations reached error. I've been wanting to take a minute to understand how watchers worked and this was great to force me into when, where, and why to use them. thanks! –  Andy McCormick Aug 12 '14 at 13:55
1  
Glad it worked. It's worth noting the differences between $watch and $watchCollection if you're learning. Watch comparisons are done every $digest too, it might be better to set the uniqueCompanies in your getCompanies() function if they won't be modified. Since you're creating new objects too, $watchCollection won't catch differences to the objects' properties, if you change a company name for instance. –  Jason Goemaat Aug 12 '14 at 20:29

I may not be fully understanding you're issue here, but it looks like you could filter the data when you get it. Such as ...

function getCompanies(){
  $http.get("get.php?table=getcompanies").success(function(data) {
    $scope.companies = data.reduce(function (prev, cur) {
      // some code for skipping duplicates goes here                              
    }, []);
  });
}

Array.reduce may not be the best way to get a new array without duplicates, but that's the general idea, anyway.

share|improve this answer
    
thanks for the response. filtering when I get my data would not work as I still need all the subsidiaries no matter their parentCompany. I guess I could dump $scope.companies into an array and reduce that, and then pull from that for my companies list, but that seems to go around and defeat the purpose of using Angularjs. –  Andy McCormick Aug 11 '14 at 20:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.