Reputation: 241
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 !
Upvotes: 1
Views: 342
Reputation: 241
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.
Upvotes: 1