通过ng-if绑定到属性指令范围属性从DOM中删除元素

时间:2016-09-20 13:40:11

标签: angularjs angularjs-directive

我认为这很简单,但它似乎不是!

我尝试创建一个通用属性指令,该指令将调用我的某个服务中的方法,并且如果服务方法返回,则有条件地导致放置它的元素不会添加到DOM中假。基本上,ng-if,但是如果内部调用服务方法并对其采取行动

Link to Plunker

我有一个包含属性指令的元素:例如

<p ng-if="visible" my-directive>Hi</p>

我在myDirective指令中将visible设置为true。我{em>期待 <p>元素在visible假的时候从DOM中删除,并在它真实的时候添加到DOM中。相反,ng-if似乎永远不会发现在指令的链接函数中已经将visible设置为true,因此<p>元素永远不会显示。

我不能100%确定它会起作用,因为指令正在移除它所存在的元素,那里有一个捕获22。

我在这方面花了太长时间,到目前为止尝试过(不成功):

  • 通过这两种方法在链接函数中添加ng-if属性
    • attr.ngIf = true;
    • element.attr('ng-if', true);
  • <p>中的ng-if更改为ng-show,从而不删除元素(我真的想要这样做)

我想知道它是否像范围一样简单?由于ng-if绑定到<p>元素的属性,因此在指令范围内设置visible会将其设置在同一范围内吗?

另一方面,我可能会过度简化,我有一种讨厌的感觉,我可能不得不考虑指令编译和转换以获得解决方案。

有没有人对我可能出错的地方有任何感觉?

2 个答案:

答案 0 :(得分:1)

tldr:显然你希望你的指令是自包含的,它应该能够删除并将自己添加到DOM中。这是可能的,并通过DOM的隔离范围手动操作最有意义(见下文)。

常规
<p ng-if="visible" my-directive>Hi</p>角度查找当前范围的visible时,该范围是指令的父范围。当定义visible时,该指令被插入到DOM中,例如取自你的掠夺者

<body ng-controller="MainCtrl">
  <p my-directive="showMe" ng-if="visible">I should be shown</p>
</body>`<br>

app.controller('MainCtrl', function($scope) {
  $scope.visible = 3;
});

会显示指令。当您在指令上定义隔离范围时

app.directive('myDirective', function() {
    return {
        restrict: 'A',
        scope: {
          myDirective: '='
         },
        link: function(scope, element, attr, ctrl) {
           scope.visible = (scope.myDirective == 'showMe') ? true : false;
         }
     }
});
指令中的

scope.visible不会影响visible考虑的ngIf

儿童范围
您可以定义子范围以访问父范围。如果你这样做,你实际上可以影响正确的visible属性,但你必须将它放在一个对象上,以便指令可以遵循范围原型链。

<body ng-controller="MainCtrl">
  <p my-directive ng-if="visibleDirectives.directive1">I should be shown</p>
</body>

$timeout用于演示目的。最初,ngIf必须评估为true,否则根本不会创建指令。

app.controller('MainCtrl', function($scope) {
  $scope.visibleDirectives = { directive1 : true };
});

app.directive('myDirective', function($timeout) {
  return {
    restrict: 'A',
    scope : true,
    link: function(scope, element, attr, ctrl) {
      console.log(scope);
      $timeout(function() {
        scope.visibleDirectives.directive1 = !scope.visibleDirectives.directive1;
        $timeout(function() {
          scope.visibleDirectives.directive1 = !scope.visibleDirectives.directive1;
        }, 2000);
      }, 2000);
    }
  }
});

这样的指令必须知道预先定义它的可见性的属性(在这种情况下是scope.visibleDirectives.visible1),这不是很实用,并且禁止了几个指令。

隔离范围
在您的示例中,您使用了隔离范围。这允许重用指令。为了使指令能够修改ngIf的相应属性,您必须再次为其提供正确的引用。

<body ng-controller="MainCtrl">
  <p my-directive="directive1" ng-if="directive1.visible">I should be shown</p>
</body>

同样,您必须在对象上提供属性,以便指令可以跟随对象引用来修改右visible

app.controller('MainCtrl', function($scope) {
  $scope.directive1 = {
      visible : true
    };
});

app.directive('myDirective', function($timeout) {
  return {
    restrict: 'A',
    scope : {
      myDirective : '='
    },
    link: function(scope, element, attr, ctrl) {
      $timeout(function() {
        scope.myDirective.visible = !scope.myDirective.visible;
        $timeout(function() {
          scope.myDirective.visible = !scope.myDirective.visible;
        }, 2000);
      }, 2000);
    }
  }
});

在这些情况下,每次ngIf计算结果为true时,都会重新创建指令。

手动操作DOM
您也可以手动删除并附加指令的节点,而无需咨询角度。

<body ng-controller="MainCtrl">
  <p my-directive>I should be shown</p>
</body>

在这种情况下,您不需要setTimeout的角度版本,甚至可以使用setInterval因为Interval只创建一次,但您必须清除它。

app.controller('MainCtrl', function($scope) { });

app.directive('myDirective', function() {
  return {
    restrict: 'A',
    scope : { },
    link: function(scope, element, attr, ctrl) {
      var el = element[0];
      var parent = el.parentNode;
      var shouldBeShown = false;
      var interval = setInterval(function() {
        var children = parent.children;
        var found = false;
        for(var i = 0; i < children.length; i++) {
          if(children[i] === el) {
            found = true;
            break;
          }
        }
        if(shouldBeShown) {
          if(!found)
            parent.appendChild(el);
        }
        else {
          if(found)
            parent.removeChild(el);
        }
        shouldBeShown = !shouldBeShown;
      }, 2000);
      scope.$on('$destroy', function() {
        clearInterval(interval);
      });
    }
  };
});

答案 1 :(得分:0)

如果要删除元素,请使用ng-show =&#34; visible&#34;这将作为布尔值进行计算,并在元素计算结果为true时显示该元素。使用&#34;!可见&#34;如果你需要翻转它。

此外,但是将scope属性添加到您的指令中,您正在添加一个额外的作用域,考虑备用时间轴,即与页面关联的控制器作用域无法看到。这可以解释为什么ng-show之前可能不适合你。