用angularjs自定义指令包装ui-select



是的,在SO上有几个类似的问题,但似乎没有一个能解决我的问题(或与最新版本的Angular 1.5.8和Angular UI-Select 1.4.x一起工作)。

我遇到的问题是双向数据绑定。当我尝试将我的模型绑定到ui-select时,如果我没有使用包装ui-select的指令,它就会起作用。然而,当我使用包装ui-select时,它会在一个方向上更新,直到我在指令中修改模型。

index . html

  <div class="form-group">
    <label class="control-label" for="TEST">TEST</label>
    <ui-select id="TEST" multiple="" ng-model="vm.selectedInstrument.gradeLevels" theme="bootstrap" close-on-select="false" append-to-body="true" style="min-width: 250px">
      <ui-select-match placeholder="THIS IS A TEST">{{$item.name}}</ui-select-match>
      <ui-select-choices repeat="opt in vm.gradeLevelList | filter:$select.search">
        <div ng-bind-html="opt.name | highlight: $select.search"></div>
      </ui-select-choices>
    </ui-select>
    <span class="help-block">
          {{vm.selectedInstrument | json}}
      </span>
  </div>
  <tag-input ng-model="vm.selectedInstrument.gradeLevels" placeholder="Select one or more..." label-text="Grade Levels" options="vm.gradeLevelList" ele-id="gradeLevelTagInput"></tag-input>

tag-input-directive.js

(function () {
    'use strict';
    angular.module('surveyexplorer')
        .directive('tagInput', tagInput);
    function tagInput() {
        return {
            restrict: 'E',
            templateUrl: 'tag-input.html',
            scope: {
                options: '=',
                editMode: '=',
                labelText: '@',
                eleId: '@s',
                placeholder: "@"
            },
            require: ['?ngModel'],
            link: function (scope, elem, attrs, ctrls) {
                var ngModelCtrl = ctrls[0];
                ngModelCtrl.$render = function() {
                    scope.innerModel = ngModelCtrl.$viewValue;
                };
                scope.$watch('innerModel', function(newval, oldval){
                   if (newval !== oldval) {
                       ngModelCtrl.$setViewValue(newval);
                   }
                });
            }
        };
    }
})();

tag-input.html

<div class="form-group">
    <label class="control-label" for="{{eleId}}">{{labelText}}</label>
    <ui-select id="{{eleId}}" multiple ng-model="innerModel" theme="bootstrap" close-on-select="false"
               append-to-body="true" style="min-width: 250px">
        <ui-select-match placeholder="{{placeholder}}">{{$item.name}}</ui-select-match>
        <ui-select-choices repeat="opt in options | filter:$select.search" class="scrollable-menu">
            <div ng-bind-html="opt.name | highlight: $select.search"></div>
        </ui-select-choices>
    </ui-select>
    <span class="help-block">
        {{innerModel}}
    </span>
</div>

script.js

(function(angular) {
  'use strict';
angular.module('surveyexplorer', [
        'ngSanitize',
        'ngAnimate',
        'ui.bootstrap',
        'ui.select',
        ])
  .controller('InstrumentCtrl', [function() {
    var vm = this;
    vm.selectedInstrument = {
      gradeLevels: []
    };
    vm.gradeLevelList = [{
        "code": "IT",
        "name": "Infant/toddler",
        "sortOrder": 1000,
        "gradeLevelId": 1
    }, {
        "code": "PR",
        "name": "Preschool",
        "sortOrder": 2000,
        "gradeLevelId": 2
    }, {
        "code": "PK",
        "name": "Prekindergarten",
        "sortOrder": 3000,
        "gradeLevelId": 3
    }, {
        "code": "TK",
        "name": "Transitional Kindergarten",
        "sortOrder": 4000,
        "gradeLevelId": 4
    }, {
        "code": "KG",
        "name": "Kindergarten",
        "sortOrder": 5000,
        "gradeLevelId": 5
    }, {
        "code": "1",
        "name": "First grade",
        "sortOrder": 6000,
        "gradeLevelId": 6
    }, {
        "code": "2",
        "name": "Second grade",
        "sortOrder": 7000,
        "gradeLevelId": 7
    }, {
        "code": "3",
        "name": "Third grade",
        "sortOrder": 8000,
        "gradeLevelId": 8
    }, {
        "code": "4",
        "name": "Fourth grade",
        "sortOrder": 9000,
        "gradeLevelId": 9
    }, {
        "code": "5",
        "name": "Fifth grade",
        "sortOrder": 10000,
        "gradeLevelId": 10
    }];
  }]);
})(window.angular);

我在这里创建了一个问题的普朗克:https://plnkr.co/edit/Yn1qhMjKuij7FM8Unpad

注意,当您向顶部控件添加标记时,它会更新底部控件中的数据模型。但是,当您向底部控件添加标记时,它将停止更新数据模型。我怀疑这与如何使用ng-model绑定模型有关。

感谢您的帮助。

这个线程和我的问题最相似:angularjs将ngModel从包装器指令传递到包装指令,但是当我试图模仿这个解决方案时,它只会让我达到我现在的地步。

所以我找到了一个解决方案,我有点理解"为什么"它工作,但不是100%确定为什么。

首先,我简化了指令:

tag-input-directive.html

(function () {
    'use strict';
    angular.module('surveyexplorer')
        .directive('tagInput', tagInput);
    function tagInput() {
        return {
            restrict: 'E',
            templateUrl: 'tag-input.html',
            scope: {
                ngModel: '=',
                options: '=',
                editMode: '=',
                labelText: '@',
                eleId: '@s',
                placeholder: "@"
            },
            controller: function($scope) {
              $scope.innerModel = $scope;
            }
        };
    }
})();

,我所做的只是将作用域中传递的值赋值给控制器函数中作用域本身的属性:

controller: function($scope) {
  $scope.innerModel = $scope;
}

然后我更新了模板中对ngModel的引用,使用innerModel.ngModel:

tag-input.html

<div class="form-group">
    <label class="control-label" for="{{eleId}}">{{labelText}}</label>
    <ui-select id="{{eleId}}" multiple ng-model="innerModel.ngModel" theme="bootstrap" close-on-select="false"
               append-to-body="true" style="min-width: 250px">
        <ui-select-match placeholder="{{placeholder}}">{{$item.name}}</ui-select-match>
        <ui-select-choices repeat="opt in options | filter:$select.search" class="scrollable-menu">
            <div ng-bind-html="opt.name | highlight: $select.search"></div>
        </ui-select-choices>
    </ui-select>
    <span class="help-block">
        {{innerModel.ngModel}}
    </span>
</div>

这是一个链接到运行的Plunkr: https://plnkr.co/edit/Eq9pIl8KoHZ2PuTa2PLu?p=preview

我怀疑ui-select中的某些东西破坏了作用域,但不太确定如何在没有大量额外工作的情况下证明或跟踪它。

最新更新