How do I apply an AngularJS directive based on a class set by ng-class?

37,641

Solution 1

ng-class just sets classes on the DOM, after the compilation process.

Perhaps a better way to apply the directive would be through an HTML attribute:

<div test-case>

Of course, this is not conditional, but I would leave the conditioning to the directive:

<div ng-app="example" ng-controller="exampleCtrl">
    <div test-case condition="dynamicCondition">Hello</div>
    <input type="checkbox" ng-model="dynamicCondition"/> Condition 
</div>

and

angular.module('example', [])
    .controller('exampleCtrl', function ($scope) {
        $scope.dynamicCondition = false;
    })
    .directive('testCase', function () {
    return {
        restrict: 'A',
        scope: {
            'condition': '='
        },
        link: function (scope, element, attrs) {
            scope.$watch('condition', function(condition){
                if(condition){
                    element.css('color', 'red');
                }
                else{
                    element.css('color', 'black');
                };
            });
        }
    }
});

Notice the directive name is testCaserather than testcase, the scope: {'condition': '='}, bit ensures that the condition attribute is synchronized and available as scope.condition and the watch evaluates the second argument every time the expression on the first changes value. JsFiddle over here.

Perhaps you should also look into ng-switch:

<div ng-switch="conditionFunction()">
  <div ng-when="true" test-case>Contents when conditionFunction() returns true</div>
  <div ng-when="false">Contents when conditionFunction() returns false</div>
</div>

Solution 2

angular.module('example', [])
  .directive('testCase', function() {
      return {
          restrict: 'C',
          link: function(scope, element, attrs) {
            element.css('color', 'red');
          }
      }
    })
Share:
37,641

Related videos on Youtube

carols10cents
Author by

carols10cents

Co-author of The Rust Programming Language book and the Rust in Motion video series.

Updated on July 09, 2022

Comments

  • carols10cents
    carols10cents almost 2 years

    I'm trying to conditionally apply a directive to an element based on its class.

    Here's a simple case of my issue, see the results in this fiddle. For this example, I'm using the map of class names to booleans form of ng-class with true; in my actual case I'd like to use the boolean result of a function.

    Markup:

    <div ng-app="example">
      <div class="testcase">
        This will have the directive applied as I expect
      </div>
      <div ng-class="{'testcase':true}">
        This will not have the directive applied but I expect it to
      </div>
    </div>
    

    JS:

    angular.module('example', [])
      .directive('testcase', function() {
          return {
              restrict: 'C',
              link: function(scope, element, attrs) {
                  element.css('color', 'red');
              }
          }
        }
      );
    

    Why isn't the directive being applied to the div that's getting its class through ng-class? Am I misunderstanding something about the order in which AngularJS is processing directives?

    How should I be conditionally applying a directive to an element based on the evaluation of an expression?

    • jdp
      jdp almost 11 years
      I wasn't able to figure out the answer, but for what it's worth, in my test, the class WAS getting toggled on and off, it just didn't apply the directive function. Why not try passing a true/false condition into the directive? i.e. <input my-directive toggle="true"> Here's my fiddle. Inspect the 2nd line and click the toggle button to show the class adding and removing. jsfiddle.net/eg4zg
    • carols10cents
      carols10cents almost 11 years
      @jdp having to do ng-class="{true: 'testcase', false: ''} [flip == true]" seems like a lot of unnecessary code when my reading of the angular docs suggest that ng-class="{'testcase': true}" should function the same way...
    • csexton
      csexton almost 11 years
      The class does not get computed until after the directives are compiled, so that may not be a good way to go about that. Seems like a better approach to this might be to load an angular template based on your object.
    • zs2020
      zs2020 almost 11 years
      @carolclarinet I created a github ticket for you github.com/angular/angular.js/issues/3854
    • carols10cents
      carols10cents almost 11 years
      thank you @sza! I've jumped into the discussion there.
  • carols10cents
    carols10cents almost 11 years
    Why is it better for the directive to decide whether to apply itself or not, when angular already has the mechanism to decide whether or not to apply a directive based on the presence or absence of an attribute, class, element, or comment?
  • Jair Trejo
    Jair Trejo almost 11 years
    I see the directive, much like a class, as defining an object. Like, "this object is a test case and should behave this way". It seems odd to me to make that identitity depend on something external.
  • Jair Trejo
    Jair Trejo almost 11 years
    In my first example, the directive decides it's behaviuor based on the attributes of the element (so it is perhaps best suited for when there is not an "all or nothing" situation). In my second example, ng-switch decides wether the element is going to be a test case or not.
  • carols10cents
    carols10cents almost 11 years
    While both your examples achieve the goal I'm trying to reach, they feel like a lot of repetitive, unnecessary code when it seems like the angular feature "set a class conditionally using ng-class and a map of classes to boolean values" and the feature "apply a directive based on the presence of a class" should be able to be combined cleanly without extra duplication. If the answer is "nope, angular just doesn't do that" then that's fine, just unexpected :)
  • Joseph Silber
    Joseph Silber almost 11 years
    Why do you need a new scope? Can't you just use the same scope as the controller?