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