Search code examples
javascriptangularjsangularjs-watch

watchcollection swallowing an attribute being watched


There are two attributes selCountry and searchText. There is a watch that monitors these two variables. The 1st one is bound to a select element, other is a input text field.

The behavior I expect is: If I change the dropdown value, textbox should clear out, and vice versa. However, due to the way I have written the watch, the first ever key press (post interacting with select element) swallows the keypress.

There must be some angular way of telling angular not to process the variable changes happening to those variables; yet still allow their changes to propagate to the view...?

  $scope.$watchCollection('[selCountry, searchText]', function(newValues, oldValues, scope){
    console.log(newValues, oldValues, scope.selCountry, scope.searchText);
    var newVal;
    if(newValues[0] !== oldValues[0]) {
      console.log('1');
      newVal = newValues[0];
      scope.searchText = '';
    }
    else if(newValues[1] !== oldValues[1]) {
      console.log('2');
      newVal = newValues[1];
      scope.selCountry = '';
    }
    $scope.search = newVal;
    var count = 0;
    if(records)
      records.forEach(function(o){
        if(o.Country.toLowerCase().indexOf(newVal.toLowerCase())) count++;
      });
    $scope.matches = count;
  });

Plunk


Solution

  • I think the problem you are encountering is that you capture a watch event correctly, but when you change the value of the second variable, it is also captured by the watchCollection handler and clears out that value as well. For instance:

    selCountry = 'Mexico'
    

    You then change

    selText = 'City'
    

    The code captures the selText change as you'd expect. It continues to clear out selCountry. But since you change the value of selCountry on the scope object, doing that also invokes watchCollection which then says "okay I need to now clear out searchText".

    You should be able to fix this by capturing changes using onChange event handlers using ng-change directive. Try the following

    // Comment out/remove current watchCollection handler.
    // Add the following in JS file
      $scope.searchTextChange = function(){
        $scope.selCountry = '';
        $scope.search = $scope.searchText;
        search($scope.search);
      };
      $scope.selectCountryChange = function(){
        $scope.searchText = '';
        $scope.search = $scope.selCountry;
        search($scope.search);
      };
    
      function search(value){
        var count = 0;
        if(records)
          records.forEach(function(o){
            if(o.Country.toLowerCase().indexOf(value.toLowerCase())) count++;
          });
        $scope.matches = count;
      }
    

    And in your HTML file

    <!-- Add ng-change to each element as I have below -->
      <select ng-options="country for country in countries" ng-model="selCountry" ng-change="selectCountryChange()">
        <option value="">--select--</option>
      </select>
      <input type="text" ng-model="searchText" ng-change="searchTextChange()"/>
    

    New plunker: http://plnkr.co/edit/xCWxSM3RxsfZiQBY76L6?p=preview