1

I know this is a pretty common problem but I searched a lot of posts and didn't find a solution to my problem.

The purpose of my directive is to minimize a sidebar responsively. In order to do this, I register media queries and add listeners to it for adding specific class but I have a problem with the two-way-binding on isMinimized that does not update parent scope.

Actually, it updates the parent scope at the initialization when _mqlMinimizeListener(mql***Devices); or _mqlUnminimizeListener(mql***Devices); are called for the first time (to detect initial size of the screen) but after value is no more updated.

Here is my directive :

angular.module('app.minimizableSideBar').directive('minimizableSideBar',
[
    '$window',
    function($window) {
        return {
            restrict: 'A',
            scope: {
                minimizeOn: '@',
                addClass: '@',
                isMinimized: '='
            },
            link: function(scope, element) {

                /**
                 * Initialize the directive and handle behavior regarding provided params
                 */
                var init = function() {

                    if (!_validParams()) {
                        return;
                    }

                    // If browser is recent
                    if ($window.matchMedia) {
                        if (scope.minimizeOn === 'object') {
                            _handleRangeBreakpoint();
                        } else {
                            scope.minimizeOn = parseInt(scope.minimizeOn);
                            _handleSimpleBreakpoint();
                        }
                    } else {
                        // If browser outdated, we fall back on innerWidth
                        scope.$watch(function() {
                            return $window.innerWidth;
                        }, function(value) {
                            if (value < scope.minimizeOn) {
                                element.addClass(scope.addClass);
                            } else {
                                element.removeClass(scope.addClass);
                            }
                        });
                    }

                    // We handle external minimization (i.e. the user want to minimize/un-minimize
                    // the sidebar by clicking on a button
                    scope.$watch('isMinimized', function(value) {
                        if (value) {
                            element.addClass(scope.addClass);
                        } else {
                            element.removeClass(scope.addClass);
                        }
                    });
                };

                /**
                 * Check params validity
                 *
                 * @returns {Boolean} true if params are valid, false otherwise
                 * @private
                 */
                var _validParams = function() {
                    if (scope.addClass &&
                        scope.minimizeOn) {

                        if (scope.minimizeOn === 'object' &&
                            (!scope.minimizeOn.lowerSize ||
                            !scope.minimizeOn.upperSize ||
                            Number.isNaN(scope.minimizeOn.lowerSize) ||
                            Number.isNaN(scope.minimizeOn.upperSize))) {
                            return false;
                        } else if (Number.isNaN(scope.minimizeOn)) {
                            return false;
                        }
                        return true;
                    }
                };

                /**
                 * Listener for minimizing action
                 *
                 * @param {MediaQueryList} mql a media query listener
                 */
                var _mqlMinimizeListener = function(mql) {
                    if (mql.matches) {
                        element.addClass(scope.addClass);
                        scope.isMinimized = true;
                    }
                };

                /**
                 * Listener for unminimizing action
                 *
                 * @param {MediaQueryList} mql a media query listener
                 */
                var _mqlUnminimizeListener = function(mql) {
                    if (mql.matches) {
                        element.removeClass(scope.addClass);
                        scope.isMinimized = false;
                    }
                };

                /**
                 * Handle Range breakpoint with lower size and higher size
                 *
                 * @private
                 */
                var _handleRangeBreakpoint = function() {
                    var lowerSize = parseInt(scope.minimizeOn.lowerSize);
                    var upperSize = parseInt(scope.minimizeOn.upperSize);

                    // Handle screen sizes
                    //-- In the range
                    var mqlRangeDevices = $window.matchMedia('screen and (min-width: ' + lowerSize + ' px) and (max-width: ' + upperSize + 'px)');
                    mqlRangeDevices.addListener(_mqlMinimizeListener);
                    _mqlMinimizeListener(mqlRangeDevices);

                    //-- Out of the range
                    var mqlInfDevices = $window.matchMedia('screen and (max-width: ' + lowerSize - 1 + 'px)');
                    mqlInfDevices.addListener(_mqlUnminimizeListener);
                    _mqlUnminimizeListener(mqlInfDevices);

                    var mqlSupDevices = $window.matchMedia('screen and (min-width: ' + (upperSize + 1) + 'px)');
                    mqlSupDevices.addListener(_mqlUnminimizeListener);
                    _mqlUnminimizeListener(mqlSupDevices);
                };

                /**
                 * Handle simple breakpoint (i.e. when mnimizeOn only contains string for one size)
                 *
                 * @private
                 */
                var _handleSimpleBreakpoint = function() {
                    var mqlInfDevices = $window.matchMedia('screen and (max-width: ' + scope.minimizeOn + 'px)');
                    mqlInfDevices.addListener(_mqlMinimizeListener);
                    _mqlMinimizeListener(mqlInfDevices);

                    var mqlSupDevices = $window.matchMedia('screen and (min-width: ' + (scope.minimizeOn + 1) + 'px)');
                    mqlSupDevices.addListener(_mqlUnminimizeListener);
                    _mqlUnminimizeListener(mqlSupDevices);
                };

                init();
            }
        };
    }
]
);

and here is the HTML markup :

<div class="sidebar navbar-collapse collapse sidebar-navbar-collapse"
     data-minimizable-side-bar
     data-minimize-on="992"
     data-add-class="sidebar-minimized"
     data-is-minimized="sidebar.isMinimized">
</div>

Thanks !

1 Answer 1

1

Ok I finally understood my mistake !

As I am updating scope from "native" Javascript listeners, I have to call scope.$apply() to get scope updated.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.