Conditionally add the “multiple” attribute to ui-s

2019-02-16 20:14发布

问题:

I'm trying to add the multiple attribute to a ui-select directive based on the value of a certain property using the ng-attr- directive. Unfortunately, that's not working for me. I've setup a plunker example to showcase what is happening.

Plunker Example

回答1:

Edit

I finally got it after reading through the mentioned GitHub Issue in the Angular Repo.

You need to set up a directive with a higher priority and a terminal attribute set to true (which skips the compilation of all other directives, after the compilation of our directive). Then in the postLink function we will compile the whole element itself. But before that our own directive needs to be removed (infinite loop!).

Big shot outs to Add directives from directive in AngularJS

Directive Code

angular.module('app')
  .directive('multiSelectChecker', function ($compile) {
    return {
      restrict: 'A',
      replace: false, 
      terminal: true, //terminal means: compile this directive only
      priority: 50000, //priority means: the higher the priority, the "firster" the directive will be compiled
      compile: function compile(element, attrs) {
        element.removeAttr("multi-select-checker"); //remove the attribute to avoid indefinite loop
        element.removeAttr("data-multi-select-checker"); //also remove the same attribute with data- prefix in case users specify data-multi-select-checker in the html

        return {
          pre: function preLink(scope, iElement, iAttrs, controller) {  },
          post: function postLink(scope, iElement, iAttrs, controller) { 
            if(scope.options.Multiple == true) {
              iElement[0].setAttribute('multiple',''); //set the multiple directive, doing it the JS way, not jqLite way.
            }
            $compile(iElement)(scope);
          }
        };
      }
    };
  });

HTML Code

  <ui-select ng-model="model.choice" multi-select-checker>
    <ui-select-match>{{$item.Title}}</ui-select-match>
    <ui-select-choices repeat="item.Id as item in options.SuggestedValues | filter: { Title: $select.search }">
      <div ng-bind="item.Title | highlight: $select.search"></div>
    </ui-select-choices>
  </ui-select>

Working Plnkr:

http://plnkr.co/edit/N11hjOFaEkFUoIyeWqzc?p=preview


Original answer (also working, but with duplicate code)

I did the following:

  1. Created a wrapping directive called multi-select-checker
  2. In that directive check wether the options.Multiple is true or false
  3. Return two different template URLs for each case. Case 1): return single-select.tpl.html or Case 2): return mutli-select.tpl.html (which includes the 'multiple' directive.

Directive code:

app.directive('multiSelectChecker', function() {
return {
    template: '<ng-include src="getTemplateUrl()"/>',
    controller: function($scope) {
      $scope.getTemplateUrl = function() {
        if($scope.options.Multiple == true) {
          console.log("multi-select");
          return "multi-select.tpl.html"
        }
        else {
          console.log("single select");
          return "single-select.tpl.html"
        }
      }
    }
  }
})

Usage in HTML:

<body ng-controller="DemoCtrl">
  <multi-select-checker>
  </multi-select-checker>
</body>

Template 1: single select

<ui-select ng-model="model.choice">
    <ui-select-match>{{$item.Title}}</ui-select-match>
    <ui-select-choices repeat="item.Id as item in options.SuggestedValues | filter: { Title: $select.search }">
        <div ng-bind="item.Title | highlight: $select.search"></div>
    </ui-select-choices>
</ui-select>

Template 2: multi-select

<ui-select ng-model="model.choice" multiple>
    <ui-select-match>{{$item.Title}}</ui-select-match>
    <ui-select-choices repeat="item.Id as item in options.SuggestedValues | filter: { Title: $select.search }">
        <div ng-bind="item.Title | highlight: $select.search"></div>
    </ui-select-choices>
</ui-select>

As you can see the two templates only differ by one single directive: 'multiple'. Maybe there are better solutions.

I even can't understand, why the ng-attr-multiple approach isn't working.

In addition I have realized, that there are two seperate input fields being rendered via the ng-attr-multiple approach.

And the single selection case seems to be broken (by removing the multiple directive) - which was in your intial Plnkr as well.

Working Code

See the working Plnkr here: http://plnkr.co/edit/T9e5tcAkcQLsDV3plfEl?p=preview



回答2:

Is this what you want to achieve:

<body ng-controller="DemoCtrl">
    This works perfectly well:
    <ui-select ng-model="model.choice" multiple>
        <ui-select-match>{{$item.Title}}</ui-select-match>
        <ui-select-choices repeat="item.Id as item in options.SuggestedValues | filter: { Title: $select.search }">
            <div ng-bind="item.Title | highlight: $select.search"></div>
        </ui-select-choices>
    </ui-select>
    <br />
    <br />
    This does not work:
    <ui-select ng-model="model.choice2" multiple="{{options.Multiple}}">
        <ui-select-match>{{$item.Title}}</ui-select-match>
        <ui-select-choices repeat="item.Id as item in options.SuggestedValues | filter: { Title: $select.search }">
            <div ng-bind="item.Title | highlight: $select.search"></div>
        </ui-select-choices>
    </ui-select>
  </body>