简体   繁体   中英

Adding extra/default option using ng-options

I am building a tag-manager in an angular form that uses two dropdown menus (in this demo a food category and a specific item). When the user selects a food category the item dropdown should appear, and when that dropdown has a value selected the I want a string added to my tag list in the format of ': '. Below is the code:

app.js

var app = angular.module('myApp', []);

app.controller('myCtrl', function($scope){

  $scope.tags = [];
  $scope.userCategory;
  $scope.userFood;
  $scope.primaryFoods = [
    {
        'id': 1,
        'parent_id': null,
        'name': 'Pizza'
    },
    {
        'id': 4,
        'parent_id': null,
        'name': 'Burgers'
    },
    {
        'id': 7,
        'parent_id': null,
        'name': 'Pasta'
    },
  ];
  $scope.secondaryFoods = [
    {
        'id': 2,
        'parent_id': 1,
        'name': 'Cheese Pizza'
    },
    {
        'id': 3,
        'parent_id': 1,
        'name': 'Combo Pizza'
    },
    {
        'id': 5,
        'parent_id': 4,
        'name': 'Cheese Burgers'
    },
    {
        'id': 6,
        'parent_id': 4,
        'name': 'Hamburgers'
    },
  ];

});

app.directive('doubleTagManager', function() {
  return {
    restrict: 'E',
    scope: {tags: '=', primary: '=', secondary: '=', userPrimary: '=', userSecondary: '='},
    templateUrl: 'double-tag-manager.html',
    link: function ($scope, $element) {
      var input = angular.element($element.find('select')[1]);
      // This adds the new tag to the tags array in '<Primary>: <Secondary>' format
      $scope.add = function() {
        var new_value = input[0].value;
        if ($scope.tags.indexOf(new_value) < 0) {
          $scope.tags.push($scope.userPrimary.name + ': ' + $scope.userSecondary.name);
        }
      };
      // This is the ng-click handler to remove an item
      $scope.remove = function (idx) {
          $scope.tags.splice(idx, 1);
      };
      input.bind( 'change', function (event) {
        $scope.$apply($scope.add);
      });
    }
  };
});

double-tag-manager.html

<div class="row">
  <div class="col-md-6">
    <select name="uFoodsPrimary" id="foodPrimary" class="form-control"
            ng-model="userPrimary"
            ng-options="item.name for item in primary track by item.name" required>
      <option value="">Select a Food category!</option>
    </select>
  </div>
  <div class="col-md-6" ng-show="userPrimary">
    <select name="uFoodsSecondary" id="foodSecondary" class="form-control"
            ng-model="userSecondary"
            ng-options="item.name for item in (secondary | filter: {parent_id: userPrimary.id})
            track by item.name"
            required>
      <option value="">Select a Food sub-category!</option>
    </select>
  </div>
</div>
<div class="tags">
  <a ng-repeat="(idx, tag) in tags" class="tag" ng-click="remove(idx)">{{tag}}</a>
</div>

What I would like to add is the ability to select 'All foods' so users don't need to select all the items individually but I cannot seem to figure out how to add an additional field using ng-options.

Fiddle

BONUS: If a category is selected that has no children I would like it added to the tags list by default.

Erik, Here is the modified code to achieve your all select feature. Further you can enhance it more to achieve your BONUS use case.

Rather than putting too much effort to achieve tagging in this way, I would suggest to use existing angularui-select2 component. It has lot of other options also. It will make your life more easy.

 var app = angular.module('myApp', []); app.controller('myCtrl', function($scope) { $scope.tags = []; $scope.sub_cat_show = false; $scope.all_sub_cat_show = false; $scope.userCategory; $scope.userFood; $scope.primaryFoods = [{ 'id': 0, 'parent_id': null, 'name': 'All Foods' }, { 'id': 1, 'parent_id': null, 'name': 'Pizza' }, { 'id': 4, 'parent_id': null, 'name': 'Burgers' }, { 'id': 7, 'parent_id': null, 'name': 'Pasta' }]; $scope.secondaryFoods = [ { 'id': 2, 'parent_id': 1, 'name': 'Cheese Pizza' }, { 'id': 3, 'parent_id': 1, 'name': 'Combo Pizza' }, { 'id': 5, 'parent_id': 4, 'name': 'Cheese Burgers' }, { 'id': 6, 'parent_id': 4, 'name': 'Hamburgers' }, ]; }); app.directive('doubleTagManager', function() { return { restrict: 'E', scope: { tags: '=', primary: '=', secondary: '=', userPrimary: '=', userSecondary: '=', sub_cat_show: '=', 'all_sub_cat_show': '=' }, template: "<div class='row'><div class='col-md-6'><select ng-change='primaryChange()' name='uFoodsPrimary' id='foodPrimary' class='form-control' ng-model='userPrimary' ng-options='item.name for item in primary track by item.name' required> <option value=''>Select a Food category!</option></select></div><div ng-show='sub_cat_show' class='col-md-6'><select ng-show='all_sub_cat_show' ng-change='secondaryChange()' name='uFoodsSecondary' id='foodSecondary' class='form-control' ng-model='userSecondary'" + //options code "ng-options='item.name for item in (secondary | filter: {parent_id: userPrimary.id}) track by item.name' required>" + //end options code "<option value=''>Select a Food sub-category!</option></select> <select ng-show='!all_sub_cat_show'><option value=''>Food all sub-category</option></select> </div></div><div><a ng-repeat='(idx, tag) in tags' class='tag' ng-click='remove(idx)'>{{tag}}</a></div>", link: function($scope, $element) { var primarySel = angular.element($element.find('select')[0]); var secondarySel = angular.element($element.find('select')[1]); // This adds the new tag to the tags array in '<Primary>: <Secondary>' format $scope.primaryChange = function() { $scope.tags = []; // reset $scope.sub_cat_show = primarySel[0].value?true:false; if (primarySel[0].value == 'All Foods') { $scope.all_sub_cat_show = false; angular.forEach($scope.primary, function(primary, index) { angular.forEach($scope.secondary, function(secondary, index) { if(secondary.parent_id==primary.id) $scope.tags.push(primary.name + ': ' + secondary.name); }); }); } else { $scope.all_sub_cat_show = true; } } $scope.secondaryChange = function() { var new_value = secondarySel[0].value; if ($scope.tags.indexOf(new_value) < 0) { $scope.tags.push(primarySel[0].value + ': ' + new_value); } }; // This is the ng-click handler to remove an item $scope.remove = function(idx) { $scope.tags.splice(idx, 1); }; } }; });
 <script src="https://code.angularjs.org/1.4.3/angular-animate.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script> <!DOCTYPE html> <html > <head> <meta charset="utf-8" /> <title>AngularJS Demo</title> <script> document.write('<base href="' + document.location + '" />'); </script> </head> <body> <div ng-app="myApp" ng-controller="myCtrl"> <double-tag-manager tags="tags" primary="primaryFoods" secondary="secondaryFoods" user-primary="userCategory" user-secondary="userFood"> </double-tag-manager> </div> </body> </html>

After looking more into stack overflow the best (and perhaps only) solution was to chain another filter that would clone the filtered array and unshift another option.

The new filter:

app.filter('addAll', function () {
  return function(input) {
    // clone the array, or you'll end up with a new "None" option added to your "values"
    // array on every digest cycle.
    var newArray = input.slice(0);
    newArray.unshift({name: "All Foods"});
    return newArray;
  };
});

Updated ng-options tag:

ng-options="item.name for item in (secondary | filter: {parent_id: userPrimary.id} | addAll)
            track by item.name"

SO post

Updated Fiddle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM