Как я могу создать директиву, которая добавляет ng-class и NG-disabled на целевом элементе на основе условия?

у меня есть следующий код:

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)

Я попытался основать код на данном ответе здесь. В принципе, я хотел бы иметь следующее:

<input ngDisableOnVar="someScopeVariable>

и заменить его следующим:

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

что-то не так, потому что, хотя я применил их к моему элементу, они всегда отключены, даже если переменная scope оценивается как true. Что я делаю не так?

изменить: Я создал plunker, где первые 2 кнопки создаются с ng-class и NG-disabled, а другие 2 кнопки должны иметь те же самые вещи, применяемые к ним с помощью директивы.

вот версия plunker с общей областью:http://plnkr.co/edit/TebCQL20ubh5AgJ6nMIl?p=preview

и вот один без общей области:http://plnkr.co/edit/CPm55MrHA8z6Bx4GbxoN?p=preview

проблема в том, что тот, у кого нет общей области, делает не обновлять. Как я могу их обновить, и условия зависят от переменных, переданных в качестве аргументов?

Изменить #2: Я начинаю верить, что совместное использование области-это правильный способ действия этих 2 кнопок, за исключением создания новой директивы, которая инкапсулирует обе кнопки внутри нее. Хотя не на 100% уверен.

4 ответов


Я бы пошел с вашим EDIT #2, потому что они связаны. Если мы создаем их как отдельные элементы, нам нужно каким-то образом передать связанный элемент каждому => когда мы нажимаем на кнопку 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">

демо

другое решение-создать родительские директивы как контейнер. Это решение мне нравится больше всего. При таком подходе вы можете свободно изменять внутреннее содержание директивы как добавление больше кнопок, больше текста,....(НЕ НУЖНО HARDCODE ШАБЛОН) родительская директива работает как менеджер, чтобы гарантировать, что есть только 1 активный ребенок одновременно:

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>

демо


вот очень уродливый способ продемонстрировать, как скомпилировать шаблон во время функции link. Это уродливо, потому что я не обращался к какой-либо привязке к переменной области. Возможно, вы захотите изолировать область или настроить двустороннюю привязку, но это должно дать вам суть доступа к области для целей компиляции.

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


вы можете получить тот же эффект с другой схожий подход. Плюх здесь

вместо $compile в функции link вы можете использовать template в вашей директиве и ng-disabled с переменной в области, которая привязана к переменной родительской области через изолированную область.


вы пытались удалить ! перед именем var?

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