How can I create a directive that adds ng-class and ng-disabled in the target element based on the condition?

I have the following code:

app.directive "ngDisableOnVar", ($compile) -> restrict: "A" terminal: true priority: 1000 replace:false scope: {} compile: compile = (element, attrs) -> cattr = attrs["ngDisableOnVar"] element.attr("ng-class", "{'disabled': !#{cattr}}") element.attr("ng-disabled", "!#{cattr}") element.removeAttr("ng-disable-on-var") pre: preLink = (scope, iElement, iAttrs, controller) -> post: postLink = (scope, iElement, iAttrs, controller) -> $compile(iElement)(scope) 

I tried to create the code in the answer here . Basically, I would like to have the following:

 <input ngDisableOnVar="someScopeVariable> 

And replace it with the following:

 <input ng-class="{'disabled': !someScopeVariable}" ng-disabled="!someScopeVariable"> 

Something is wrong, because even if I applied them to my element, they are always disabled, although the scope variable is true. What am I doing wrong?

EDIT: I created a plunker where the first 2 buttons are created with an ng-class and ng-disabled, and the remaining 2 buttons should have the same things applied to them using the directive.

Here is a version of plunker with a common scope: http://plnkr.co/edit/TebCQL20ubh5AgJ6nMIl?p=preview

And here is one without a common area: http://plnkr.co/edit/CPm55MrHA8z6Bx4GbxoN?p=preview

The problem is that one who does not have a common area is not updated. How can I update them and the conditions depend on the variables passed as arguments?

EDIT No. 2: I'm starting to believe that sharing a region is the right way these 2 buttons should act without creating a new directive that encapsulates both buttons inside it. Not 100% sure.

+6
source share
4 answers

I would go with your EDIT # 2 because they are connected . If we create them as separate elements, we need to somehow transfer the associated element to each of them => When we click on button 1, we update ourselves, as well as the associated element.

Here I changed your first approach to make it work: http://plnkr.co/edit/KgYIlATiw9xzTEZt9Jv1?p=preview

In this example, I have to pass the related item to each directive so that when we click, we can update ourselves and the related item:

 related-element="btnForward" 

I made some changes to the directive:

 scope: { reactOn: "=", //use property binding instead of function binding relatedElement:"@" }, link: function(scope, element, attrs) { scope.toggle = function(){ scope.reactOn = !scope.reactOn;//toggle current element var relatedScope = $("#"+scope.relatedElement).scope();//get related element scope and toggle it relatedScope.reactOn = !relatedScope.reactOn; } //var cattr = attrs.ngDisableReactOn; element.attr("ng-class", "{'disabled': !reactOn}"); //Use reactOn instead as this is the property of current scope element.attr("ng-disabled", "!reactOn"); element.attr("ng-click", "toggle()"); element.removeAttr("ng-disable-react-on"); $compile(element)(scope); } 

We do not need to make things complicated. Just create a normal directive to wrap 2 buttons.

 myApp.directive("ngDisableReactOn", function($compile) { return { restrict: "A", templateUrl:"ngDisableReactOn.html", scope: { can_go_back: "@" }, link: function(scope, element, attrs) { scope.goBack = function(){ scope.can_go_back = false; } scope.goFwd = function(){ scope.can_go_back = true; } } } }); 

Template:

 <input type="button" value="go back" ng-click="goBack()" ng-class="{'disabled': !can_go_back}" ng-disabled="!can_go_back"> <input type="button" value="go fwd" ng-click="goFwd()" ng-class="{'disabled': can_go_back}" ng-disabled="can_go_back"> 

Demo

Another solution is to create a parent directive as a container. This is the solution I like best. With this approach, you can freely change the internal contents of the directive, for example, add more buttons, more text, .... ( DO NOT REMOVE TEMPLATION ). The parent directive works as a manager to ensure that there is only one active child at a time:

 myApp.directive("ngDisableReactOnContainer", function() { //Container directive to manage all child directives return { restrict: 'EA', replace: true, transclude: true,//Use transclusion to move inner content to the template template: '<div ng-transclude></div>', controller: function() { var children = []; this.selectChild = function(activeChild) { //ensure that only 1 child is active at a time activeChild.active = true; angular.forEach(children, function(child) { if (child != activeChild) { child.active = false; } }); } this.addChild = function(child) { children.push(child); } } }; }); myApp.directive("ngDisableReactOn", function($compile) { return { restrict: "A", scope:{ active:"@" }, require: '^?ngDisableReactOnContainer', link: function(scope, element, attrs, controller) { scope.active = scope.active === 'true'; controller.addChild(scope);//register itself with the container scope.select = function(){//When this element is clicked, inform the container to toggle all children accordingly. controller.selectChild(scope); } //Add ng-class and ng-disabled based on your requirement. element.attr("ng-class", "{'disabled': active}"); //Use active instead as this is the property of current scope element.attr("ng-disabled", "active"); element.attr("ng-click", "select()"); element.removeAttr("ng-disable-react-on"); $compile(element)(scope); } } }); 

Using these directives would be simple:

 <div ng-disable-react-on-container> <input ng-disable-react-on type="button" value="button 1" active="true" > <input ng-disable-react-on type="button" value="button 2" > <input ng-disable-react-on type="button" value="button 3" > </div> 

Demo

+5
source

Here is a very ugly way to demonstrate how to compile a template during a link function. This is terrible because I did not pay any binding to the variable region. You might want to select a region or establish two-way binding, but this should give you the essence of accessing the region for compilation purposes.

 app.directive('foo', function($compile) { return function(scope, elem, attrs) { var html; if (scope.testVar) html = '<input ng-class="{\'disabled\': !someScopeVariable}" ng-disabled="!someScopeVariable" />'; else html = '<input />'; var htmlEl = angular.element(html), compiledEl = $compile(htmlEl)(scope); elem.replaceWith(compiledEl); } }); 

http://plnkr.co/edit/xBS4ZMXVwqv8CwWvwTu5?p=preview

+1
source

You can get the same effect with a different similar approach. Plunk here

Instead of $compile in the link function, you can use template in your directive and ng-disabled with a variable in scope bound to the parent variable of the scope through an isolated scope.

+1
source

You tried to delete ! before the name var?

 <input ng-class="{'disabled': someScopeVariable}" ng-disabled="someScopeVariable"> 
-2
source

All Articles