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 一样工作以防止每次重新呈现它。
您可以通过同时使用 ngIf
和 ngShow
来使其工作,其中每个都由不同的变量控制。 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>
编辑: 上面的版本可以通过一次性绑定进一步改进(和简化)以减少 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-if
和 ng-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);
想象一下可能会在网页上呈现的一些沉重的内容,例如图表。 Angular 提供了 2 个选项来切换所述内容的可见性。
ng-show 将呈现内容而不考虑表达式,并在事后简单 "hide" 它。这并不理想,因为用户在会话期间可能永远不会 "open" 内容,因此渲染它是一种浪费。
ng-if在这方面比较好。如果表达式为 false,使用它代替 ng-show 将首先阻止重内容的渲染。然而,它的优势也是它的弱点,因为如果用户隐藏图表然后再次显示,每次都会从头开始呈现内容。
我怎样才能制定一个两全其美的指令?这意味着它像 ng-if 一样工作,直到第一次呈现内容,然后切换到像 ng-show 一样工作以防止每次重新呈现它。
您可以通过同时使用 ngIf
和 ngShow
来使其工作,其中每个都由不同的变量控制。 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>
编辑: 上面的版本可以通过一次性绑定进一步改进(和简化)以减少 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-if
和 ng-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);