AngularJS directive only when the condition is true

15,978

Solution 1

Can you do something like this, using ng-if?

<ul ng-controller="ListViewCtrl" >
   <li ng-repeat="item in items">
      <span>{{item.name}}</span>
      <div contextmenu ng-if="item.hasMenu"></div>
   </li>
</ul>

Here are the docs for ng-if.

EDIT: If you are driving the context menu off of a class, you should be able to do this:

<ul ng-controller="ListViewCtrl" >
   <li ng-class="{'hasmenu': item.hasMenu}" ng-repeat="item in items">{{item.name}} </li>
</ul>

Solution 2

I think this is pretty tricky if you don't want to change your DOM structure. If you could just place your contextmenu directive on a sub DOM node inside the <li> things would be a lot easier.

However, let's assume you can't do that and let's also assume that you don't own the contextmenu directive so that you can't change it to your needs.

Here is a possible solution to your problem that might be a bit hackish (actually I don't know!)

'use strict';

angular.module('myApp', [])

  .controller('TestController', ['$scope', function($scope) {
    $scope.items = [
        {name:1, hasMenu: true}, 
        {name:2, hasMenu: false }, 
        {name:3, hasMenu: true}
      ];
  }])
  .directive('contextmenu', function(){
    return {
      restrict: 'A',
      link: function(scope, element){
        element.css('color', 'red');
      }
    }
  })
  .directive('applyMenu', ['$compile', function($compile){

    return {
      restrict: 'A',
      link: function(scope, element){
        if (scope.item.hasMenu){
          //add the contextmenu directive to the element
          element.attr('contextmenu', '');
          //we need to remove this attr
          //otherwise we would get into an infinite loop
          element.removeAttr('apply-menu');

          //we also need to remove the ng-repeat to not let the ng-repeat 
          //directive come between us.
          //However as we don't know the side effects of 
          //completely removing it, we add it back after
          //the compile process is done.
          var ngRepeat = element.attr('ng-repeat');
          element.removeAttr('ng-repeat');
          var enhanced = $compile(element[0])(scope);
          element.html(enhanced);
          element.attr('ng-repeat', ngRepeat);
        }
      }
    }
  }]);

I faked the contextmenu directive to just change the color to red just so that we can see it's taking place.

Then I created an apply-menu attribute directive. This directive than checks if the hasMenu property is true and if so hooks in and adds the contextmenu directive and does a manual $compile process.

However, what worries me a bit about this solution is that I had to temporally remove the ng-repeat directive (and also the apply-menu directive) to get the $compile process to act the way we want it to act. We then add the ng-repeat directive back once the $compile has been made. That is because we don't know the side effects of removing it entirely from the resulting html. This might be perfectly valid to do, but it feels a bit arkward to me.

Here is the plunker: http://plnkr.co/edit/KrygjX

Share:
15,978
lostpacket
Author by

lostpacket

Updated on June 13, 2022

Comments

  • lostpacket
    lostpacket about 2 years

    I am going to have a contextmenu directive in ng-repeat items. Based on whether a condition is true, the directive should be applied. How do I put a condition like only when item.hasMenu == true then apply the directive ?

    <ul ng-controller="ListViewCtrl" >
    <li contextmenu ng-repeat="item in items">{{item.name}} </li>
    </ul>
    

    EDIT

    This seems to have worked for me. First the directive.

    app.directive('menu',function(){
    
        return {
            restrict : 'A',
    
            link : function(scope,element,attrs){
    
                if(scope.hasMenu){
                            element.contextmenu({
                                            menu:[
                                            {title:"Remove" , "cmd" : "remove"},
                                            {title:"Add" , "cmd" : "add"},
                                            ],
                                            select:function(event,ui){
                                                //alert("select " + ui.cmd + " on" + ui.target.text());
                                                if (ui.cmd ==='remove'){
                                                    alert('Remove selected on ' + scope.item);
                                                }
                                                if (ui.cmd ==='add'){
                                                    alert("Add selected");
                                                }
                                            }
                            });
                }
    
            }
        }
        }
    );
    

    Then the html

     <ul ng-controller="ListViewCtrl" >
    <li menu  ng-repeat="item in items">{{item.name}} </li>
    </ul>