【问题标题】:Angular one time binding without a watchAngular 一次性绑定,无需手表
【发布时间】:2017-10-26 06:51:55
【问题描述】:

我在使用 Angular 的一次性绑定时遇到问题。

假设我想使用一次绑定的 ngIf,如下所示:

<div ng-if="::showImage">
    <img src="somesource" img-preloader/>
</div>

在这种情况下,angular 会为 if 中的表达式创建一个监视。 一旦它被解析为一个非未定义的值,手表就会被移除。

如果仅将其解析为真实值,则将后代 html 树添加到 DOM 并随后呈现。

现在这一切都很好,但我真的很想避免最初的手表,只需解析表达式,如果它未定义 - 然后才设置手表。原因在我的场景中相当复杂,但基本上我有一些机制可以暂时禁用不需要的手表......

所以我一直在寻找内置 angular 一次性绑定的替代品,结果遇到了angular-once

Angular-once 以不同的方式实现一次性绑定,它仅在表达式被解析为 undefined 时设置临时监视,因此如果它在初始尝试中解析,则不会创建监视。听起来不错。

所以我可以这样做:

<div once-if="showImage">
    <img src="somesource" img-preloader/>
</div>

但是,问题出在这里——显然,后代 HTML 树默认首先呈现,然后如果一次性解析为 false,则后代节点将从 DOM 中删除。

这是执行此操作的 sn-p:

{
  name: 'onceIf',
  priority: 600,
  binding: function (element, value) {
    if (!value) {
      element.remove();
    }
  }
},

这对我来说是不好的行为,因为创建后代树是不可行的,并且会导致其他问题,例如 - 在上面的示例中,img 将被下载。

因此,我正在寻找一种在 ngIf 等指令中进行一次性绑定的方法,如果表达式解析成功且无需预渲染后代树,则无需设置监视。

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    我试图避免这种情况,但现在我最终实现了基于 Angular 标准指令的自定义指令,但具有必要的附加功能。

    ngIf 派生指令:

    app.directive('watchlessIf', ['$animate', '$compile', '$parse', function($animate, $compile, $parse) {
        return {
            multiElement: true,
            transclude: 'element',
            priority: 600,
            terminal: true,
            restrict: 'A',
            $$tlb: true,
            link: function($scope, $element, $attr, ctrl, $transclude) {
                function valueChangedAction(value) {
                    if (value) {
                        if (!childScope) {
                            $transclude(function(clone, newScope) {
                                childScope = newScope;
                                clone[clone.length++] = $compile.$$createComment('end watchlessIf', $attr.watchlessIf);
                                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).then(function() {
                                previousElements = null;
                            });
                            block = null;
                        }
                    }
                }
    
                var block, childScope, previousElements;
                if ($attr.watchlessIf.startsWith("::")) {
                    var parsedExpression = $parse($attr.watchlessIf)($scope);
                    if (parsedExpression != null) {
                        valueChangedAction(parsedExpression);
                        return;
                    }
                }
    
                $scope.$watch($attr.watchlessIf, valueChangedAction);
            }
        };
    }]);
    

    ngBind 派生指令:

    app.directive('watchlessBind', ['$compile', '$parse', function($compile, $parse) {
        return {
            restrict: 'AC',
            compile: function watchlessBindCompile(templateElement) {
                $compile.$$addBindingClass(templateElement);
                return function watchlessBindLink(scope, element, attr) {
                    function valueChangedAction(value) {
                        element.textContent = (typeof value == "undefined") ? '' : value;
                    }
    
                    $compile.$$addBindingInfo(element, attr.watchlessBind);
                    element = element[0];
                    if (attr.watchlessBind.startsWith("::")) {
                        var parsedExpression = $parse(attr.watchlessBind)(scope);
                        if (parsedExpression != null) {
                            valueChangedAction(parsedExpression);
                            return;
                        }
                    }
    
                    scope.$watch(attr.watchlessBind, valueChangedAction);
                };
            }
        };
    }]);
    

    注意事项:

    • 不幸的是,使用这种方法,我将不得不为其他 Angular 指令实现类似的指令,并且我想支持潜在的无手表一次性绑定。

    • 我在指令中使用了私有角度的东西,比如 $$tlb 选项,虽然我真的不应该...

    【讨论】:

      猜你喜欢
      • 2016-03-26
      • 2014-07-21
      • 1970-01-01
      • 2014-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-17
      相关资源
      最近更新 更多