5
\$\begingroup\$

This directive works fine but I have the feeling that I'm not doing it the "angular way" with .attr('hide', 'true'). Is there a better way to show or hide elements from a directive?

app.directive('myRandomshow', function() {
  // Show a random element from child elements

  function randomIntFromInterval(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
  }
  return {
    link: function(scope, element) {
      // Ensure everything is initialy hidden
      element.children().attr('hide', 'true');
      scope.$on('event::event', function() {
        // Show result
        element.children().attr('hide', 'true');
        element.children().eq(randomIntFromInterval(2, element.children().length - 1)).removeAttr('hide');
      });
    }
  };
});
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Correct me if this is incorrect but it appears that this code utilizes jQuery methods like .children(), .attr(), .eq() and .removeAttr(). Many would argue that this is far from the "angularJS way" of developing such code.

As this answer explains:

AngularJS is a framework that handles "data binding" for you, meaning it will (and should) take charge of keeping your model (e.g. $scope) and view (the markup) in synch. You should usually not interfere with this behavior, unless there is a very specific reason to do so. A quite lengthy but interesting read on this and related topics can be found in this answer (which incidentally was answered to a question about when it is okay to use jQuery yourself).

Long story short: don't update the DOM inside your controller / scope.

Instead of updating the DOM using jQuery methods, the ngHide directive could be used. In order to do that from the directive one might need to utilize properties stored in the controller that would control the values used for the ng-hide attribute on each child element.

A demonstration of this is below. There are three buttons labeled 1, 2 and 3 and all are hidden by default. Clicking the button above them will randomly select one button to show (and of course hide the others, similar to the original code).

angular.module('sampleDirective', [])
  .controller('Controller', ['$scope', function($scope) {
    $scope.children = Array(3).fill(0).map((value, id) => ({
      id: id + 1,
      hidden: false
    }));
  }])
  .directive('myRandomshow', function() {
    // Show a random element from child elements
    function randomIntFromInterval(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min);
    }
    return {
      link: function(scope, element) {
        scope.children.forEach(child => child.hidden = true);
        scope.$on('event::event', function() {
          // Show result
          const indexToShow = randomIntFromInterval(1, scope.children.length);
          scope.children.forEach((child, index) => child.hidden = (index + 1) != indexToShow);
        });
      }
    };
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="sampleDirective">
  <div ng-controller="Controller">
    <button ng-click="$emit('event::event')">Show a randomly selected child</button>
    <div my-randomshow>
      <button ng-repeat="(key, child) in children track by key" ng-hide="child.hidden">{{ child.id }}</button>
    </div>
  </div>
</div>

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.