1

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/

3
  • 2
    Why don't you simply use select? By the way, have a look at the documentation. Commented Aug 10, 2014 at 20:32
  • I'm looking to provide a user experience that feels like: jsfiddle.net/qbt2myj8 (updated question as well) Commented Aug 11, 2014 at 19:40
  • 1
    Yep. So I repeat my question! Commented Aug 11, 2014 at 20:26

2 Answers 2

2

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 :)

Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.