title: How to create angular ui-select directive wrapper which binds property of the selected item to a model
body: <p>I am trying to create angular directive which is a wrapper on top of ui-select directive. My goal is to give the directive a list with items and a model which will sync with a particular property of the selected item(s). My directive has 5 attributes:</p>
  
  <pre>
  ng-model - to specify the model,
  items - to specify the list of items,
  display-prop - the name of the property which will be used inside ui-select for display purpose,
  value-prop - the name of the property which will be used for model assignment,
  multiple [optional] - if multiple selection is allowed
  </pre>
  
  <p>In the directive child scope I have object which syncs with ui-select ng-model and when it is changed I am updating the main scope.</p>
  
  <p>I manage to make the things working for a single selected item and for multiple items when you start with empty selection. However I still have a problem to display the initial selected items when a multiple selection is chosen. I think the problem is somewhere with the scopes and $watch methods between my directive and ui-select directive. It looks like updates in my directive scope does not affect ui-select ng-model in the case of array. 
  <br/>
  I've create a <a href="http://plnkr.co/edit/lBFDVbiMOQ7hfjcKTFU3?p=preview" rel="nofollow">Plunker</a> with simple application containing the directive and test case where you can see that a single selection works correctly but when I have an array the list is not initialized.</p>
  
  <pre><code>angular.module('dgUi', ['ui.select', 'ngSanitize'])
  
    .config(['uiSelectConfig', function(uiSelectConfig){
      uiSelectConfig.theme = 'select2';
    }])
  
    .controller('UiSelectWrapperConttoller', ['$scope', function($scope){
      $scope.userAddresses = [
        {
          address: 'Address 1',
          description: 'Home address'
        },
        {
          address: 'Address 2',
          description: 'Office address'
        },
        {
          address: 'Address 3',
          description: 'Test address 3'
        },
        {
          address: 'Address 4',
          description: 'Test address 4'
        }
      ];
  
      $scope.currentUser = {
        name: 'User 1',
        address: 'Address 1'
      };
  
      $scope.currentUser = {
        name: 'User 1',
        address: 'Address 2',
        availableAddresses:['Address 3']
      };
    }])
  
    .directive('dgSelect', ['$parse', function ($parse) {
          return {
              restrict: 'AE',
              require: ['ngModel'],
              scope: true,
              templateUrl: function(tElement, tAttrs) {
                  return '/global/dg-ui/dg-select' + ((angular.isDefined(tAttrs.multiple) ? '-multi' : '') + '.tpl.html');
               },
              compile: function (tElement, tAttrs) {
                var displayPropSufix = tAttrs.displayProp ? '.' + tAttrs.displayProp : '';
                  var isMultiple = angular.isDefined(tAttrs.multiple) ;
  
                  //match element config
                  if (tAttrs.placeholder) {
                      $('ui-select-match, *[ui-select-match]', tElement).attr('placeholder', tAttrs.placeholder);
                  }
  
                  if(isMultiple){
                      $('ui-select-match, *[ui-select-match]', tElement).html('{{$item' + displayPropSufix + '}}');
                  }else{
                      $('ui-select-match, *[ui-select-match]', tElement).html('{{$select.selected' + displayPropSufix + '}}');
                  }
  
  
                  //choices element config
                  $('ui-select-choices, *[ui-select-choices]', tElement).attr('repeat', 'listItem in ' + tAttrs.items + ' | filter:$select.search')
                  $('ui-select-choices, *[ui-select-choices]', tElement).html('&lt;div ng-bind-html="listItem' + displayPropSufix + ' | highlight: $select.search"&gt;&lt;/div&gt;');
                  return function link(scope, element, attrs, ctrls) {
                      scope.ngModel = ctrls[0];
                      scope.isMultiple = angular.isDefined(attrs.multiple) 
                      scope.itemsGetter = $parse(attrs.items);
                      if(angular.isDefined(attrs.valueProp) &amp;&amp; attrs.valueProp !== ''){
                          scope.valuePropGetter = $parse(attrs.valueProp);
                      }
  
                      scope.getValueMapper = function(itemObject){
                          return scope.valuePropGetter ? scope.valuePropGetter(itemObject) : itemObject;
                      }
  
  
                      scope.updateValueFromModel = function(modelValue){
                          if(scope.isMultiple){
                              var selectionArray = [];
                              angular.forEach(modelValue, function(modelItem, key){
                                  var modelItemValue = scope.getValueMapper(modelItem);
                                  selectionArray.push(modelItemValue);
                              });
                              scope.selectionModel = selectionArray;
                          }else{
                              var items = scope.itemsGetter(scope);
                              angular.forEach(items, function(item, key){
                                  var itemValue = scope.getValueMapper(item);
                                  if(itemValue == modelValue){
                                      scope.selectionModel = item;
                                      return false;
                                  }
                              });
                          }
                      }
  
                      if(scope.isMultiple){
                          scope.$watchCollection(attrs.ngModel, function(modelValue, oldValue) {
                              scope.updateValueFromModel(modelValue);
                          }); 
                      }else{
                          scope.$watch(attrs.ngModel, function(modelValue){
                              scope.updateValueFromModel(modelValue);
                          });
                      }
  
                      //watch the items in case of async loading
                      //scope.$watch(attrs.items, function(){
                      //  scope.updateValueFromModel(scope.ngModel.$modelValue);
                      //});
  
                      scope.onItemSelect = function(item, model){
                          var movelValue = scope.getValueMapper(item);
                          if(scope.isMultiple){
                              scope.ngModel.$viewValue.push(movelValue);
                          }else{
                              scope.ngModel.$setViewValue(movelValue);
                          }
                      }
  
                      scope.onItemRemove = function(item, model){
                          var removedModelValue = scope.getValueMapper(item);
                          if(scope.isMultiple){
                            var removeIndex = null;
                            angular.forEach(scope.ngModel.$viewValue, function(itemValue, index){
                                if(itemValue == removedModelValue){
                                      removeIndex = index;
                                      return false;
                                  }
                              });
                              if(removeIndex){
                                scope.ngModel.$viewValue.splice(removeIndex, 1);
                              }
                          }else{
                              scope.ngModel.$setViewValue(movelValue);
                          }
                      }
  
                  }
              }
          };
      }])
  
  .run(['$templateCache', function ($templateCache) {
          $templateCache.put('/global/dg-ui/dg-select.tpl.html',
            '&lt;ui-select class="ui-select" ng-model="selectionModel" on-select="onItemSelect($item, $model)" on-remove="onItemRemove($item, $model)" ng-disabled="disabled"&gt;&lt;ui-select-match&gt;&lt;/ui-select-match&gt;&lt;ui-select-choices&gt;&lt;/div&gt;&lt;/ui-select-choices&gt;&lt;/ui-select&gt;');
          $templateCache.put('/global/dg-ui/dg-select-multi.tpl.html',        '&lt;ui-select class="ui-select" multiple ng-model="selectionModel" on-select="onItemSelect($item, $model)" on-remove="onItemRemove($item, $model)" ng-disabled="disabled"&gt;&lt;ui-select-match&gt;&lt;/ui-select-match&gt;&lt;ui-select-choices&gt;&lt;/ui-select-choices&gt;&lt;/ui-select&gt;');
      }]);
  </code></pre>
  
  <p>Probably I am doing something wrong here. I would greatly appreciate any help :), thanks!</p>