簡體   English   中英

如何在AngularJS中實現反向一次綁定ng-if表達式?

[英]How to implement reverse one time bind ng-if expression in AngularJS?

我有一個自定義的AngularJS組件,可以在單個網頁上使用200多次。 該頁面最終實現了4000多名觀察者-這比AngularJS希望的最大觀察者數量更多-並使該頁面真正變慢。

實際的問題是,組件模板中的某些ng-if和其他AngularJS表達式遺留了許多不需要的觀察者,它們不再需要更改其值。

對於普通的ng-if ,修復很容易:

<div ng-if="::$ctrl.isInitialized()">Ready!</div>

...其中$ctrl.isInitialized()將返回true (初始化組件時)或undefined (直到它被undefined )。

在這里返回undefined將使AngularJS保持觀察者處於活動狀態,直到它返回其他值(在這種情況下為true ,然后將div添加到DOM中。

沒有ng-not="expression"就像ng-hide 這對ng-hide效果很好,除了在控制器初始化之后div仍然位於DOM中之外,這不是完美的解決方案。

但是,如何實現它,以使<div>在DOM中一直存在,直到控制器已初始化並在之后將其刪除?

盡管沒有ng-not指令,但是很容易從AngularJS源代碼中實現:

var ngNotDirective = ['$animate', '$compile', function($animate, $compile) {

  function getBlockNodes(nodes) {
    // TODO(perf): update `nodes` instead of creating a new object?
    var node = nodes[0];
    var endNode = nodes[nodes.length - 1];
    var blockNodes;

    for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
      if (blockNodes || nodes[i] !== node) {
        if (!blockNodes) {
          blockNodes = jqLite(slice.call(nodes, 0, i));
        }
        blockNodes.push(node);
      }
    }

    return blockNodes || nodes;
  }

  return {
    multiElement: true,
    transclude: 'element',
    priority: 600,
    terminal: true,
    restrict: 'A',
    $$tlb: true,
    link: function($scope, $element, $attr, ctrl, $transclude) {
      var block, childScope, previousElements;
      $scope.$watch($attr.ngNot, function ngNotWatchAction(value) {
        if (!value) {
          if (!childScope) {
            $transclude(function(clone, newScope) {
              childScope = newScope;
              clone[clone.length++] = $compile.$$createComment('end ngNot', $attr.ngNot);
              // Note: We only need the first/last node of the cloned nodes.
              // However, we need to keep the reference to the jqlite wrapper as it might be changed later
              // by a directive with templateUrl when its template arrives.
              block = {
                clone: clone
              };
              $animate.enter(clone, $element.parent(), $element);
            });
          }
        } else {
          if (previousElements) {
            previousElements.remove();
            previousElements = null;
          }
          if (childScope) {
            childScope.$destroy();
            childScope = null;
          }
          if (block) {
            previousElements = getBlockNodes(block.clone);
            $animate.leave(previousElements).done(function(response) {
              if (response !== false) previousElements = null;
            });
            block = null;
          }
        }
      });
    }
  };
}];

此方法與ng-if相同,但已恢復if (!value)檢查。

可以這樣使用:

<div ng-not="::$ctrl.isInitialized() ? true : undefined">Loading...</div>

通過在$ctrl.isInitialized()添加console.log()可以很容易地驗證是否存在無用的觀察程序-該函數將被調用幾次,直到返回true並刪除觀察程序為止-以及div及其內部的任何內容。

一種快速修補程序:我猜想,角度允許v1.1.5之后的表達式中的三元運算符。

因此,您可以進行如下操作:

<div ng-if="::$ctrl.isInitialized() === undefined? undefined: !$ctrl.isInitialized()">

據我所知, undefined在角度表達式中沒有特殊含義-在$scope中將其視為另一個(尚未定義)變量。 所以我必須明確地把它放在那兒:

$scope = undefined;

另一種選擇是編寫短助手:

function isDefined(val) {
    return angular.isDefined(val) || undefined;
}

以后使用

ng-if="::isDefined($ctrl.isInitialized()) && !$ctrl.isInitialized()"

但是,由於您說的地方太多了-請確保按照上面的代碼制作自己的組件看起來更好

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM