angular-ui/ui-select: how to set initial selected object correctly
Asked Answered
S

1

8

This is my ui select in the view:

<ui-select ng-model="selectedLabel.selected" ng-disabled="fetchingLabels || working">
    <ui-select-match placeholder="">{{$select.selected.code}}</ui-select-match>
    <ui-select-choices repeat="label in labels| filterBy: ['name', 'code']: $select.search"> 
        <div ng-bind-html="label.code | highlight: $select.search"></div>
        <small ng-bind-html="label.name | highlight: $select.search"></small>
    </ui-select-choices>
</ui-select>

And this is the relevant code in my controller:

$scope.labels = [];
$scope.selectedLabel = {};
$scope.selectedLabel.selected = $scope.passedLabel; // This is an object passed 
                                                    // from the previous controller.
                                                    // The scope comes with it.


$scope.fetchLabels(); // This fetches the labels from the server
                      // and puts them in $scope.labels

The labels brought from the server are theoretically like these:

[{'labelId': 20, 'code': 'L20', 'name': 'some label'},
 {'labelId': 21, 'code': 'L21', 'name': 'other label'}, ...]

And the passed from-outside label, 'passedLabel', is theoretically like ONE of those in $scope.labels too, eg:

  passedLabel = {'labelId': 21, 'code': 'L21', 'name': 'other label'}


...I say theoretically because, empirically, I'm seeing that they are different because of the things that angular adds to them (eg. $$hashKey, or __proto__).

So, because of that difference, the $scope.selectedLabel.selected = $scope.passedLabel isn't matching the corresponding item in the ui-select (they are not the same object), and thus, the result of that is this behavior:

ui-select behavior with initial selection

How can I set the initial selection correctly? is there a way I can use id's instead of object comparisson? I want to avoid having a for like this:

  for (i=0; i<$scope.labels; i++) {
      if ($scope.labels[i].labelId == $scope.passedLabel.labelId) {
           $scope.selectedLabel.selected = $scope.labels[i]
      }
  }

which I'm pretty sure it would work as expected, but I would have to call that for after the ajax has returned... and I have other ui-selects too

Sirreverence answered 24/11, 2014 at 3:57 Comment(2)
Did you find a solution for this problem other then the for loop you suggested? I'm having the same problem and I really don't like the search & replace thing for the initial value. A live demo of the issue can be found at plnkr. When using a regular drop down we can achieve this via a track by expression, angular material can do it via ng-model="foo" ng-model-options="{ trackBy: '$value.id' }". I really want to do something similar with ui-select ...Facile
no, I didn't find a solution, sorry... I ended up repeating the selected element, that is: The initial selected item is at the top (well, selected) and if you scroll down you can find it again somewhere in the list.Sirreverence
M
5

If you want to achieve the state that you have mentioned, then simply pass the correct reference to your model.

So after the success of the fetchlabel call you set the values in labels. Now in the success of this function you need to call the function that fetches presentLabel.

As soon as you get the data of present label you can get the index of that object in the labels scope.

var idx;
_.find($scope.labels, function(label, labelIdx){ 
  if(label.labelId == parentLabel.labelId){ idx = labelIdx; return true;}; 
});

$scope.selectedLabel = {};
$scope.selectedLabel.selected = $scope.labels[idx];

This will solve your purpose.

Martainn answered 24/11, 2014 at 4:47 Comment(2)
the _.find() is somehow similar to the for I had at the end of my post?Sirreverence
Yes you shall get same result. However underscore is much recommended utility as it gives readabilityMartainn

© 2022 - 2024 — McMap. All rights reserved.