1

I am in the process of learning how to make custom directives in AngularJS.

I have this simple program that works well and I would like to refactor it into a custom directive. I would like the directive element to be the div that is a child of the body tag. I am having trouble imagining how the slider values would be passed up and down the DOM from the input tags to the div and the other way around.

Code:

<html ng-app="DualSlidersApp">
<head>
  <meta charset="UTF-8">
  <title>DualSlidersApp</title>
  <style>
    .indented { margin-left: 61px; }
  </style>
  <script src="js/angular.js"></script>
  <script>
    'use strict';
    var app = angular.module('DualSlidersApp', []);
    app.controller('DualSlidersController', ['$scope', function($scope) {
      $scope.firstRangeSliderValue  = 10;
      $scope.secondRangeSliderValue = 20;
      $scope.reconcileSliders = function reconcileSliders(drivenByWhichSlider) {
        if ($scope.firstRangeSliderValue > $scope.secondRangeSliderValue) {
          if (drivenByWhichSlider === 'drivenByTheFirstSlider') {
            $scope.secondRangeSliderValue = $scope.firstRangeSliderValue;
          } else {
            $scope.firstRangeSliderValue = $scope.secondRangeSliderValue;
          }
        }
      }
    }]);
  </script>
</head>
<body ng-controller="DualSlidersController">
<div>
  <table>
    <tr>
      <td>
        <input type="range" min="10" max="30" step="2"
               ng-model="firstRangeSliderValue"
               ng-change="reconcileSliders('drivenByTheFirstSlider');">
      </td>
      <td>{{ firstRangeSliderValue }}</td>
    </tr>
    <tr>
      <td>
        <input type="range" min="20" max="40" step="2" class="indented"
               ng-model="secondRangeSliderValue"
               ng-change="reconcileSliders('drivenByTheSecondSlider');">
      </td>
      <td>{{ secondRangeSliderValue }}</td>
    </tr>
  </table>
</div>
</body>
</html>
3
  • You said that you would like the div to be the directive, and the input fields are inside that div, so what do you mean by "how the slider values would be passed up and down the DOM from the input tags to the div and the other way around"? did you mean how would the values be passed to the controller and the other way around? Commented Dec 22, 2015 at 19:11
  • @Someonation My assumption is that when I define the link function on the div I would need both interesting values of the widget-as-a-whole to be attributes of the div wrapper, however they belong to HTML tags deep inside the div, so how do I 'symlink' them at the div level (for lack of better terminology). Commented Dec 22, 2015 at 20:47
  • do you mean, for instance, supply '10' as an attribute in the div wrapper and set the first div's min attribute to 10? Commented Dec 22, 2015 at 21:56

1 Answer 1

2

I'd assume you meant sharing data between the directive and the controller when you wrote "how the slider values would be passed up and down the DOM from the input tags to the div and the other way around".

Binding is probably the best use of angular that you can implement here. bind 2 variables from the controller's scope to the directive's scope, each one represents the value of each slider. I would also suggest to pass a function to the directive's scope as well, so the ng-change would be calledback in the controller. It should look something like this:

directive.js

app.directive('dualSlidersDirective', function() {
  return {
    scope: {
      sliderOneValue: '=',
      sliderTwoValue: '=',
      onSliderChange: '&'
    },
    templateUrl: 'directive.html'
  };
});

directive.html:

<div>
  <table>
    <tr>
      <td>
        <input type="range" min="10" max="30" step="2"
               ng-model="sliderOneValue"
               ng-change="onSliderChange({drivenByWhichSlider: \'drivenByTheFirstSlider\'})">
      </td>
      <td>{{ sliderOneValue }}</td>
    </tr>
    <tr>
      <td>
        <input type="range" min="20" max="40" step="2" class="indented"
               ng-model="sliderTwoValue"
               ng-change="onSliderChange({drivenByWhichSlider: \'drivenByTheSecondSlider\'})">
      </td>
      <td>{{ sliderTwoValue }}</td>
    </tr>
  </table>
</div>

And don't forget to bind the relevant variables and function from your controller's scope in the main html, notice that functions receive their arguments as a hash (the key being the name of the parameter and the value being the value of it). it is very important to keep the same names of the variables throughout the script

So the entire thing should look something like this:

<html ng-app="DualSlidersApp">
<head>
  <meta charset="UTF-8">
  <title>DualSlidersApp</title>
  <style>
    .indented { margin-left: 61px; }
  </style>
  <script src="js/angular.js"></script>
  <script>
    'use strict';
    var app = angular.module('DualSlidersApp', []);
    app.controller('DualSlidersController', ['$scope', function($scope) {
      $scope.firstRangeSliderValue  = 10;
      $scope.secondRangeSliderValue = 20;
      $scope.reconcileSliders = function reconcileSliders(drivenByWhichSlider) {
        if ($scope.firstRangeSliderValue > $scope.secondRangeSliderValue) {
          if (drivenByWhichSlider === 'drivenByTheFirstSlider') {
            $scope.secondRangeSliderValue = $scope.firstRangeSliderValue;
          } else {
            $scope.firstRangeSliderValue = $scope.secondRangeSliderValue;
          }
        }
      }
    }]);
    app.directive('dualSlidersDirective', function() {
      return {
        scope: {
          sliderOneValue: '=',
          sliderTwoValue: '=',
          onSliderChange: '&'
        },
        template:             
            '<div>' +
              '<table>' +
                '<tr>' +
                  '<td>' +
                    '<input type="range" min="10" max="30" step="2"' +
                           'ng-model="sliderOneValue"' +
                           'ng-change="onSliderChange({drivenByWhichSlider: \'drivenByTheFirstSlider\'})">' +
                  '</td>' +
                  '<td>{{ sliderOneValue }}</td>' +
                '</tr>' +
                '<tr>' +
                  '<td>' +
                    '<input type="range" min="20" max="40" step="2" class="indented"' +
                           'ng-model="sliderTwoValue"' +
                           'ng-change="onSliderChange({drivenByWhichSlider: \'drivenByTheSecondSlider\'})">' +
                  '</td>' +
                  '<td>{{ sliderTwoValue }}</td>' +
                '</tr>' +
              '</table>' +
            '</div>'
      };
    });
  </script>
</head>
<body ng-controller='DualSlidersController'>
  <dual-sliders-directive slider-one-value='firstRangeSliderValue' slider-two-value='secondRangeSliderValue' on-slider-change='reconcileSliders(drivenByWhichSlider)'></dual-sliders-directive>
</body>
</html>
Sign up to request clarification or add additional context in comments.

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.