即使使用apply,AngularJS指令也不会更新范围值

时间:2016-01-21 18:40:57

标签: angularjs

我是指示在屏幕尺寸小于600px时在屏幕上显示div。问题是,即使在指令中使用$apply(),范围值也不会更新。

这是代码:

function showBlock($window,$timeout) {
    return {
        restrict: 'A',
        scope: true,
        link: function(scope, element, attrs) {
            scope.isBlock = false;
            checkScreen();

            function checkScreen() {
                var wid = $window.innerWidth;
                if (wid <= 600) {
                    if(!scope.isBlock) {
                        $timeout(function() {
                            scope.isBlock = true;
                            scope.$apply();
                        }, 100);
                    };

                } else if (wid > 600) {
                    if(scope.isBlock) {
                        $timeout(function() {
                            scope.isBlock = false;
                            scope.$apply();
                        }, 100);
                    };
                };
            };
            angular.element($window).bind('resize', function(){
                checkScreen();
            }); 
        }
    };
}

HTML:

<div ng-if="isBlock" show-block>
    //..conent to show
</div>
<div ng-if="!isBlock" show-block>
    //..other conent to show
</div>

注意:如果我不使用$ timeout,我将收到错误

  

$ digest已在进行中

我在里面使用了控制台日志来检查它是否正在更新值,并且在指令中一切正常。但是这些变化并不适用于观点。该块未显示。

3 个答案:

答案 0 :(得分:1)

在这种情况下,您应该使用do rule来获得Prototypal Inheritance of AngularJS的优势。

基本上你需要创建一个具有各种属性的对象。就像你的情况一样,你可以$scope.model = {},然后在其中放置isBlock属性。因此,当您进入指令时,您将可以访问父作用域。它背后的原因是,你有scope: true,它表示在指令中创建的是原型范围继承的原型。这意味着您的子范围中可以使用所有引用类型对象。

<强>标记

<div ng-if="model.isBlock" show-block>
    //..conent to show
</div>
<div ng-if="!model.isBlock" show-block>
    //..other conent to show
</div>

<强>控制器

app.controller('myCtrl', function($scope){
   //your controller code here

   //here you can have object defined here so that it can have properties in it
   //and child scope will get access to it.
   $scope.model = {}; //this is must to use dot rule,
   //instead of toggle property here you could do it from directive too
   $scope.isBlock = false; //just for demonstration purpose

});

然后在你的指令中你应该使用scope.model.isBlock而不是scope.isBlock

<强>更新

在代码中使用controllerAs模式时,需要使用scope.ag.model.isBlock。这将为您提供在指令中获取范围变量值的访问权限。

基本上,您可以获取父控制器值(使用controllerAs模式)在子级内部生成可用的控制器值。您可以在$scope内找到包含控制器别名的对象。就像这里一样,你创建了ag作为控制器别名,所以你需要scope.ag.model来获取指令链接函数中的model值。

注意

您不需要将$apply$timeout一起使用,这可能会导致错误$apply in progress,因此$ timeout会为您运行摘要,您无需担心运行摘要。

Demo Here

答案 1 :(得分:0)

我怀疑它与如果ng-if =&#34; isBlock&#34;并且不会触发show-block指令这一事实有关。永远不会是真的,所以它永远不会注册resize事件。

答案 2 :(得分:0)

根据我的经验,线性代码永远不适用于动态DOM属性,例如窗口大小调整。使用寻找屏幕大小的代码,您需要将其放在某种事件/ DOM观察者中,例如角度我用$ watch来观察尺寸。所以要解决这个问题,你需要将代码放在$ watch中,例如下面。我没有测试过这段代码,只是方向性。您可以观看$ window.innerWidth或者您可以观看$ element例如身体取决于你的目标。我说这是因为屏幕将遍布整个地方但是如果你控制一个DOM元素,比如身体,你可以更好地控制。为了简洁起见,我也没有使用$ timeout。

// watch window width
showBlock.$inject = ['$window'];
function bodyOverflow($window) {
    var isBlock = false;
    return {
        restrict: 'EA',
        link: function ($scope, element, attrs) {
            $scope.$watch($window.innerWidth, function (newWidth, oldWidth) {
                if (newWidth !== oldWidth) {
                    return isBlock = newWidth <= 600;
                }
            })
        }
    };
}

// OR watch element width
showBlock.$inject = [];
function bodyOverflow() {
    var isBlock = false;
    return {
        restrict: 'EA',
        link: function ($scope, element, attrs) {
            $scope.$watch($element, function (new, old) {
                if (newWidth) {
                    return isBlock = newWidth[0].offsetWidth <= 600;
                }
            })
        }
    };
}