2

How can I get the Broken example (below) to work without changing it from an array (as I have a lot of code that depends on it being an array in other parts of the application)?

I'd like to continue using an array but I'd like the correct select option to show when the model changes. The model changes correctly when the button is clicked but the option isn't being found in the select (ng-model and ng-options just aren't matching up when it's an array).

angular.module('app', [])
  .controller('MainCtrl', ['$scope', function($scope) {
  
    $scope.valueWorking = 'value1';
    $scope.selectFilterWorking = [{name: 'name1', value: 'value1'},{name: 'name2', value: 'value2'}];
    
    $scope.valueBroken = ['value1'];
    $scope.selectFilterBroken = [{name: 'name1', value: ['value1']},{name: 'name2', value: ['value2', 'value3']}];
    
  }]);
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<div ng-app="app" ng-controller="MainCtrl">

  <strong>Working</strong> (SELECT <i>is</i> updated when ng-model is a string):<br>
  <select ng-model="valueWorking" 
          ng-options="select.value as select.name for select in selectFilterWorking">
    <option value="">Select an Option</option>
  </select>
  <button ng-click="valueWorking='value2'">Assign to String of 'value2'</button> {{valueWorking}}<br><br>
  
  <strong>Broken</strong> (SELECT <i>not</i> updated when ng-model is an array):<br>
  <select ng-model="valueBroken" 
          ng-options="select.value as select.name for select in selectFilterBroken">
    <option value="">Select an Option</option>
  </select>
  <button ng-click="valueBroken=['value2','value3']">Assign to Array of ["value2","value3"]</button> {{valueBroken}}
  
</div>

Upon clicking 'Run code snippet' above, notice that the initial option is not selected (on the Broken select, when it's an array), but is initially selected on the Working select when it's a string. The correct option is also not shown when 'Assign to Array of ["value2"]' is clicked.

UPDATE: I'm trying to get the OPTIONS ($scope.selectFilterBroken) to match the MODEL, rather than the other way around - hence my need to keep the model as an array. The button click is merely to simulate the other parts of the application that manipulate the array.

Any help would be great. Thanks and please.

2 Answers 2

3

Objects in Javascript are not equal even though they look identical but strings or other primitive types are. For example, var a = [1]; var b = [1]; a === b returns false. However, if you do JSON.stringify(a) === JSON.stringify(b), it will return true.

When you assign ['value2'] to valueBroken, valueBroken is redeclared as a new array so it doesn't match with the one in the selectFilterBroken.

To fix this, we wanna find a way to assign the reference of ['value2'] to valueBroken. I wrote a function to find which array in selectFilterBroken matches the one we want by comparing their stringified versions. Once found, assign the reference of the one in the selectFilterBroken to valueBroken.

Without knowing what exactly is going on in your app, it is hard to guess if the solution fits you, but you can go from there.

angular.module('app', [])
  .controller('MainCtrl', ['$scope', function($scope) {
  
    $scope.valueWorking = 'value1';
    $scope.selectFilterWorking = [{name: 'name1', value: 'value1'},{name: 'name2', value: 'value2'}];
    
    $scope.valueBroken = ['value1'];
    $scope.selectFilterBroken = [{name: 'name1', value: ['value1']},{name: 'name2', value: ['value2']}];

    $scope.selectBroken = function(valueAry){
    for(var i = 0, m = $scope.selectFilterBroken.length; i < m; i++){
       if(JSON.stringify($scope.selectFilterBroken[i].value) === JSON.stringify(valueAry)){

       $scope.valueBroken = $scope.selectFilterBroken[i].value;
       break;
    }
    }
    
    }
  }]);
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<div ng-app="app" ng-controller="MainCtrl">

  <strong>Working</strong> (SELECT <i>is</i> updated when ng-model is a string):<br>
  <select ng-model="valueWorking" 
          ng-options="select.value as select.name for select in selectFilterWorking">
    <option value="">Select an Option</option>
  </select>
  <button ng-click="valueWorking='value2'">Assign to String of 'value2'</button> {{valueWorking}}<br><br>
  
  <strong>Broken</strong> (SELECT <i>not</i> updated when ng-model is an array):<br>
  <select ng-model="valueBroken" 
          ng-options="select.value as select.name for select in selectFilterBroken">
    <option value="">Select an Option</option>
  </select>
  <button ng-click="selectBroken(['value2'])">Assign to Array of ["value2"]</button> {{valueBroken}}
  
</div>

Sign up to request clarification or add additional context in comments.

6 Comments

I'm trying to go the other way around, modify $scope.selectFilterBroken to match $scope.valueBroken = ['value1'];. Apologies if not clear in my original question - I'll update at some point.
sure! would like to see what problem it is being solved
I just updated, tweaked the code too - hopefully, explaining myself better. I think you're going down the right path and helping, thank you! I did try getterSetter logic and got much closer, stackoverflow.com/a/28224980/3389046.
I created a plunker of my gettersetter solution that's half working... plnkr.co/edit/mo8y49aqBfXwlttMtyn1?p=preview
just to clarify, are you saying that we wanna make the select work without using stringify?
|
2

The reason its not binding is because the array value on $scope.valueBroken is different from the arrays in the $scope.selectFilterBroken. For them to match they have to be in the same array. For example, I have edited your code and assigned the value of an item in $scope.selectFilterBroken to $scope.valueBroken. And it is working fine now.

angular.module('app', [])
  .controller('MainCtrl', ['$scope', function($scope) {
  
    $scope.valueWorking = 'value1';
    $scope.selectFilterWorking = [{name: 'name1', value: 'value1'},{name: 'name2', value: 'value2'}];
    
    
    $scope.selectFilterBroken = [{name: 'name1', value: ['value1']},{name: 'name2', value: ['value2']}];
    $scope.valueBroken = $scope.selectFilterBroken[0].value;
    
  }]);
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<div ng-app="app" ng-controller="MainCtrl">

  <strong>Working</strong> (SELECT <i>is</i> updated when ng-model is a string):<br>
  <select ng-model="valueWorking" 
          ng-options="select.value as select.name for select in selectFilterWorking">
    <option value="">Select an Option</option>
  </select>
  <button ng-click="valueWorking='value2'">Assign to String of 'value2'</button> {{valueWorking}}<br><br>
  
  <strong>Broken</strong> (SELECT <i>not</i> updated when ng-model is an array):<br>
  <select ng-model="valueBroken" 
          ng-options="select.value as select.name for select in selectFilterBroken">
    <option value="">Select an Option</option>
  </select>
  <button ng-click="valueBroken=['value2']">Assign to Array of ["value2"]</button> {{valueBroken}}
  
</div>

1 Comment

I'm trying to go the other way around, modify $scope.selectFilterBroken to match $scope.valueBroken = ['value1'];. Apologies if not clear in my original question - I'll update at some point.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.