AngularJS scope doesn't update until do something in ui, like click a object with a function

3.5k views Asked by At

I am trying to add class to my sidebar when the viewport is less than or equal to 641px, and I have to watch the width of the window for that case

    $scope.$watch(function(){
       return $window.innerWidth;
    }, function(value) {
        if (value <= 641) {
            logger.info('!!!less than 641');
            vm.singleColumn = true;
        };
    });

It logs on first load, but when I resize, I have to do some clicks before it triggers again. E.g. I resized then I clicked on an item with ng-click behavior then that is the only time, it logs again.

I've read some of the questions, and it is likely due to $digest and $apply?

Can someone give a light to my confusion.

3

There are 3 answers

0
Dmitry Tolmachov On

1) What user1162084 says

2) The approach with watch on function(){return $window.innerWidth;} will not work, because the resize of window do not cause the start of $digest cycle.

The $watch expression is reevaluated only in $digest cycle. No $digest cycle = no reevaluation. In angularjs $digest cycle is started:

a) After code in ng-click was executed

b) After function in $timeout or $interval was executed.

c) After http request made with $http was finished and success\error handler was executed

There may be and other cases, but the point is that resize of window do not belong to those types of events, which lead to start of $digest cycle.

And this also explains why with code you provided you get update only after ng-click execution

0
user1162084 On

According to me you can try directive and check the window resize using jquery and update your variable accordingly. I got an example here please check

var app = angular.module('miniapp', []);

function AppController($scope) {
    /* Logic goes here */
}

app.directive('resize', function ($window) {
    return function (scope, element) {
        var w = angular.element($window);
        scope.getWindowDimensions = function () {
            return {
                'h': w.height(),
                'w': w.width()
            };
        };
        scope.$watch(scope.getWindowDimensions, function (newValue, oldValue) {
            scope.windowHeight = newValue.h;
            scope.windowWidth = newValue.w;

            scope.style = function () {
                return {
                    'height': (newValue.h - 100) + 'px',
                        'width': (newValue.w - 100) + 'px'
                };
            };

        }, true);

        w.bind('resize', function () {
            scope.$apply();
        });
    }
})
div {
    border:1px solid red;
}
<div ng-app="miniapp" ng-controller="AppController" ng-style="style()" resize>window.height: {{windowHeight}}
    <br />window.width: {{windowWidth}}
    <br />
</div>

fiddle

please let me know if this works

0
dz210 On

You need to trigger the digest cycle, or the view does not realize that the value was updated.

$scope.$watch(function(){
   return $window.innerWidth;
}, function(value) {
    if (value <= 641) {
        logger.info('!!!less than 641');
        vm.singleColumn = true;
        $scope.$apply();
    };
});