jgreenberg
jgreenberg

Reputation: 478

angular custom multiselect list

I want to build a list that functions like a select or a multiselect given a param. eg. if the parameter is false, select A->select B, unselects A and selects B.

 <ul>
      <li>a,</li>
      <li>b, and</li>
      <li>c</li>
</ul>

My initial approach looks something like:

<ul myDirective>
      <li ng-model="a">a,</li>
      <li ng-model="b">b, and</li>
      <li ng-model="c">c</li>
</ul>

Where mydirective watches for clicks on the LIs. On Click it determines if it needs to unselect or unset anything. Then selects the new value an and applies a selected class.

I don't think I'm follows the angular paradigms and I'm looking for some help finding the right path forward.

** Edit ** I'm looking to provide a user experience like: http://jsfiddle.net/qbt2myj8/

Upvotes: 1

Views: 1634

Answers (2)

mcn
mcn

Reputation: 721

in my opinion a custom directive will make your code much clean since you already use AngularJS.

Below is a complete solution using AngularJS Custom Directive that is completely reusable for code organization.

HTML

custom directive default behavior

<ul data-my-directive class="list">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

(This is the screenshot preview only. See PLAYGROUND link below to play around)

Example 1


HTML

with custom attribute

<ul data-my-directive data-multi-select="true" class="list">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

(This is the screenshot preview only. See PLAYGROUND link below to play around)

Example 2


Javascript

angular.module('app',[])
  .directive('myDirective', function(){
    return {
      transclude: true,
      template: '<div ng-transclude></div>',
      link: function(scope, element, attrs){
        function addClickListener(i, callback){
          selections[i].addEventListener('click', function(){
            callback(i);
          }, false);
        }

        // Set up attribute value. Goes to default if attribute is not set up
        attrs.multiSelect = attrs.multiSelect || 'false';

        scope.isSelected = [];

        var selections = element[0].getElementsByTagName('li');

        if(attrs.multiSelect === "true"){
          for(var i = 0; i < selections.length; i++){
            scope.isSelected[i] = false;
            addClickListener(i, function(i){
              if(scope.isSelected[i] === true){
                // if previously it is selected (red color), now make it unselected (black color / default color)
                angular.element(selections[i]).removeClass('selected');
                scope.isSelected[i] = false; 
              } else {
                // previously black color, so change it to red color
                angular.element(selections[i]).addClass('selected');
                scope.isSelected[i] = true;
              }
            });
          }
        } else {
          var currentSelection = -1;
          for(var i = 0; i < selections.length; i++){
            scope.isSelected[i] = false;
            addClickListener(i, function(i){
              if(scope.isSelected[i] === true){
                // do nothing
              } else {
                // previously black color, so change it to red color
                angular.element(selections[i]).addClass('selected');
                scope.isSelected[i] = true;

                angular.element(selections[currentSelection]).removeClass('selected');
                scope.isSelected[currentSelection] = false;
                currentSelection = i;
              }
            });
          }
        }
      }
    }
  });

And finally, the ultimate

PLAYGROUND

to play around!

I hope it clears up your requirements. Have a nice day :)

Upvotes: 2

Valentin Waeselynck
Valentin Waeselynck

Reputation: 6051

My approach would be (at first) not to make a directive at all, but a custom JavaScript data structure. Quick example out of my mind :

/**
 * Makes a selector data structure
 * @param items elements to be listed
 * @param multi whether multiple selection is supported
 * @returns {{cells: *, selectedItems: selectedItems, unselectAll: unselectAll}}
 */
function makeSelector (items, multi) {

  var cells = items.map(function (item) {
    // each cell wraps an item and a selected flag
    return  {
      item: item,
      selected: false
    };
  });

  // returns an array of the currently selected items
  function selectedItems() {
    return cells
      .filter(function (cell) {
        return cell.selected;
      })
      .map(function (cell) {
        return cell.item;
      });
  }

  // unselects all items
  function unselectAll() {
    cells.forEach(function (cell) {
      cell.selected = false;
    })
  }

  // adding methods to cells
  cells.forEach(function (cell) {
    cell.selectMe = (multi
      ? function () {
      cell.selected = true;
    }
      : function () {
      unselectAll();
      cell.selected = true;
    });
    cell.unselectMe = function () {
      cell.selected = false;
    };

    cell.toggle = function () {
      if (cell.selected) {
        cell.unselectMe();
      } else {
        cell.selectMe();
      }
    }
  });

  return {
    cells: cells,
    selectedItems: selectedItems,
    unselectAll: unselectAll
  };
}

This will allow you to easily retrieve your list of selected items in your controller:

// ...
$scope.abcSelector = makeSelector(['A','B','C'],false);
var getSelectedItems = $scope.abcSelector.selectedItems; // to do something with them in your controller
// ...

Then use Angular data-binding to reflect this data-structure in your HTML:

    <h3>List of items :</h3>
    <ul>
        <li ng-repeat="cell in abcSelector.cells" ng-click="cell.toggle()"
            ng-class="{'active': cell.selected}">
            {{cell.item}}
        </li>
    </ul>

    <h3>List of selected items :</h3>
    <ul>
        <li ng-repeat="item in abcSelector.selectedItems()">
            {{item}}
        </li>
    </ul>

and if you fell like it factor this out in a directive, which will typically bind the list of the selected items to an ng-model.

Upvotes: 1

Related Questions