Angular ng-if 和 ng-show 组合

Angular ng-if and ng-show combination

想象一下可能会在网页上呈现的一些沉重的内容,例如图表。 Angular 提供了 2 个选项来切换所述内容的可见性。

ng-show 将呈现内容而不考虑表达式,并在事后简单 "hide" 它。这并不理想,因为用户在会话期间可能永远不会 "open" 内容,因此渲染它是一种浪费。

ng-if在这方面比较好。如果表达式为 false,使用它代替 ng-show 将首先阻止重内容的渲染。然而,它的优势也是它的弱点,因为如果用户隐藏图表然后再次显示,每次都会从头开始呈现内容。

我怎样才能制定一个两全其美的指令?这意味着它像 ng-if 一样工作,直到第一次呈现内容,然后切换到像 ng-show 一样工作以防止每次重新呈现它。

您可以通过同时使用 ngIfngShow 来使其工作,其中每个都由不同的变量控制。 ngIf 将被设置为 true 一次,再也不会设置为 false,而 ngShow 将根据用户的需要进行切换。

看看这个fiddle

+1 丹尼斯的回答,但为了完整起见,甚至可以通过在没有 "polluting" 控制器的情况下将逻辑保留在视图中来进一步简化它:

<button ng-click="show = !show">toggle</button>
<div ng-if="once = once || show" ng-show="show">Heavy content</div>

plunker

编辑: 上面的版本可以通过一次性绑定进一步改进(和简化)以减少 once 上不必要的 $watch - 这只会起作用在 Angular 1.3+:

<div ng-if="::show || undefined" ng-show="show">Heavy content</div>

需要undefined来确保监视值在变为true之前不会"stabilize"。一旦稳定下来,它也会失去 $watch,因此它不会受到 show.

的任何进一步更改的影响。

@new-dev 的回答启发了我自己的组合想法,即使用切换按钮快速获得相当重的组件。

我最初的问题是我的页面由大约 20 个组件组成,每个组件加载时间大约为 1 秒。每个都由一个按钮切换。

如果使用普通的 ng-if,我会立即加载页面并在按下切换键后延迟 1 秒。

如果使用普通的 ng-show,我会得到即时切换按钮,但页面加载延迟 20 秒...

所以,现在我也将它们与 ng-mouseenter 控件结合起来。像这样。

<div ng-mouseenter="create=true">
    <button ng-click="showAll = !showAll"></button>
    <!--lightweight content-->
    <div ng-show="showAll">
        <div ng-if="create">
            <!--Heavy content-->
        </div>
    </div>
</div>

这让我在 chrome 和 IE 中都表现出色。用户在进入组件后实际单击按钮所花费的时间用于 DOM 创建。然后 DOM 节点会在(如果)点击到达时快速显示。

我也再也没有将 ng-if 表达式设置为 false。如果用户不断切换该部分,则保持 DOM 缓存。

更新: 我没有在同一个 div 上使用 ng-if 和 ng-show 的原因是在 IE 中它会导致闪烁。当 mouseenter 事件到达时,它会显示内容然后隐藏它。在 chrome 中可以将它放在同一个 div.

您可能对 Alan Colver 的自定义指令 ng-lazy-show 感兴趣。它允许组合 ng-ifng-show:

<div ng-lazy-show="showFilters" lendio-business-filters></div>

代码很小,可以很容易地添加到您的项目中:

var ngLazyShowDirective = ['$animate', function ($animate) {

  return {
    multiElement: true,
    transclude: 'element',
    priority: 600,
    terminal: true,
    restrict: 'A',
    link: function ($scope, $element, $attr, $ctrl, $transclude) {
      var loaded;
      $scope.$watch($attr.ngLazyShow, function ngLazyShowWatchAction(value) {
        if (loaded) {
          $animate[value ? 'removeClass' : 'addClass']($element, 'ng-hide');
        }
        else if (value) {
          loaded = true;
          $transclude(function (clone) {
            clone[clone.length++] = document.createComment(' end ngLazyShow: ' + $attr.ngLazyShow + ' ');
            $animate.enter(clone, $element.parent(), $element);
            $element = clone;
          });
        }
      });
    }
  };

}];

angular.module('yourModule').directive('ngLazyShow', ngLazyShowDirective);