如何根据条件创建在目标元素上添加ng-class和ng-disabled的指令?

时间:2014-01-31 16:09:00

标签: javascript angularjs

我有以下代码:

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)

我尝试将代码基于here给出的答案。基本上,我想要有以下内容:

<input ngDisableOnVar="someScopeVariable>

并将其替换为以下内容:

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

有些东西是错误的,因为即使我将它们应用于我的元素,它们也总是被禁用,即使范围变量的计算结果为true。我做错了什么?

编辑: 我创建了一个plunker,其中前两个按钮是使用ng-class和ng-disabled创建的,另外两个按钮应该通过使用该指令在它们上应用相同的东西。

以下是具有共享范围的plunker版本:http://plnkr.co/edit/TebCQL20ubh5AgJ6nMIl?p=preview

这是没有共享范围的人:http://plnkr.co/edit/CPm55MrHA8z6Bx4GbxoN?p=preview

问题是,没有共享范围的人不会更新。如何让它们更新,并且条件取决于作为参数传递的变量?

编辑#2:编辑#2: 我开始相信范围共享是这两个按钮应该采取行动的正确方式,而不是创建一个封装其中的两个按钮的新指令。不过100%肯定。

4 个答案:

答案 0 :(得分:5)

我会选择你的编辑#2,因为他们是相关的。如果我们将它们创建为单独的元素,我们需要以某种方式将相关元素传递给每个元素=&gt;当我们点击1按钮时,我们会更新自己以及相关元素。

我在这里修改了您的第一种方法:http://plnkr.co/edit/KgYIlATiw9xzTEZt9Jv1?p=preview

在这个例子中,我必须将相关元素传递给每个指令,这样当我们点击时我们可以更新自己和相关元素:

related-element="btnForward"

我在指令中做了一些修改:

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's 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);
    }

我们不需要让事情变得复杂。只需创建一个普通指令来包装2个按钮。

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;
      }
    }
  }
});

模板:

<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

另一种解决方案是将父指令创建为容器。这是我最喜欢的解决方案。使用这种方法,您可以自由更改指令的内部内容,例如添加更多按钮,更多文本,....(不需要硬编码模板)父指令作为管理器工作确保一次只有一个活跃的孩子:

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);
    }
  }
});

使用这些指令很简单:

<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

答案 1 :(得分:1)

这是一种非常难看的方式来演示如何在链接函数期间编译模板。这很难看,因为我没有解决范围变量的任何绑定问题。您可能希望隔离范围或设置双向绑定,但这应该为您提供如何访问范围以进行编译的要点。

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

答案 2 :(得分:1)

您可以使用其他类似方法获得相同的效果。 Plunk here

而不是链接函数中的$compile,您可以在指令中使用template,在ng-disabled中使用通过隔离范围绑定到父作用域变量的作用域中的变量。

答案 3 :(得分:-2)

您是否尝试删除var名称前的!

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