Search code examples
javascripthtmlangularjsangular-ngmodel

Using ng-model with select to show data attribute instead of value?


I currently have a simple select list that contains both data attributes and a value:

<select ng-model="myList">
    <option value="1" data-name="Test name 1">Option 1</option>
    <option value="2" data-name="Test name 2">Option 2</option>
    <option value="3" data-name="Test name 3">Option 3</option>
</select>

<div id="displaySelected">
    {{myList}}
</div>

Inside my div (with the ID of displaySelected), it is currently displaying the value of the selected option.

How can I change this to display the data-name attribute instead?


Solution

  • I am not sure of the complete data structure and other possibilities that are not mentioned in the question. But here is a simple solution for this static list.

    app.directive('dataselector', function(){
     return{
        restrict:'A',
        require:['ngModel','select'], //Require ng-model and select in order to restrict this to be used with anything else
        link:function(scope, elm, attrs, ctrl){
          //get configured data to be selected, made this configurable in order to be able to use it with any other data attribs
          var dataProp = attrs.dataselector,
              ngModel = ctrl[0]; 
    
          elm.on('change', handleChange);//Register change listener
    
          scope.$on('$destroy', function(){
            elm.off('change', handleChange);
          });
    
          function handleChange(){
            //get the value
            var value = this.value;
            //get the dataset value
            var dataVal = elm[0].querySelector('option[value="' + this.value + '"]').dataset[dataProp];
            //reset ngmodel view value
            ngModel.$setViewValue(dataVal);
            ngModel.$render();
            //set element value so that it selects appropriate item
            elm.val(value);
          }
        }
      }
    });
    

    and use it as:

     <select ng-model="myList" dataselector="name">
    

    Note: If you are using a different way(that you are not showing us) to build the select options based on a data structure that has the value populated in the data attribute (like an ng-repeat), you could register change event using ng-change="selectedItem(itemFromRepeat)" and you can set another property with the data value in the handler and use it.

    var app = angular.module('plunker', []);
    
    app.controller('MainCtrl', function($scope) {
    
    }).directive('dataselector', function() {
    
      return {
        restrict: 'A',
        require: ['ngModel', 'select'],
        link: function(scope, elm, attrs, ctrl) {
          var dataProp = attrs.dataselector;
          elm.on('change', handleChange);
    
          scope.$on('$destroy', function() {
            elm.off('change', handleChange);
          });
    
          function handleChange() {
            var value = this.value;
            var dataVal = elm[0].querySelector('option[value="' + this.value + '"]').dataset[dataProp];
            ctrl[0].$setViewValue(dataVal);
            ctrl[0].$render();
            elm.val(value);
          }
        }
      }
    
    });
    <!DOCTYPE html>
    <html ng-app="plunker">
    
    <head>
      <meta charset="utf-8" />
      <title>AngularJS Plunker</title>
      <script>
        document.write('<base href="' + document.location + '" />');
      </script>
      <link rel="stylesheet" href="style.css" />
      <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
      <script src="app.js"></script>
    </head>
    
    <body ng-controller="MainCtrl">
      <select ng-model="myList" dataselector="name">
        <option value="1" data-name="Test name 1">Option 1</option>
        <option value="2" data-name="Test name 2">Option 2</option>
        <option value="3" data-name="Test name 3">Option 3</option>
      </select>
      <data-select></data-select>
      <div id="displaySelected">
        {{myList}}
      </div>
    </body>
    
    </html>