Angular Responsive Layout Directive

By Marina Lohova

January 13, 2015

To all of you window.onResize aficionados, I dedicate this blog post because today we will be doing a lot of dynamic resizing in JavaScript. All of it will be done completely and effortlessly with my one-page long Angular directive.

Why do I need to attach an expensive onResize handler to my already overloaded page, you ask. The answer is very simple. Our app layout is pixel-perfect. Each element has the predefined width and margins. Yet, the app needs to look good on all kind of devices, from regular PC to tablet to iPhone. That’s why I created the following Angular directive in /scripts/directives/tsResize.js:

angular.module('angularApp') .directive('tsResize', function($window) { return function(scope, element) { var w = angular.element($window); scope.getWindowDimensions = function () { return { 'h': $window.innerHeight, 'w': $window.innerWidth }; }; scope.$watch(scope.getWindowDimensions, function (newValue, oldValue) { scope.windowHeight = newValue.h; scope.windowWidth = newValue.w; scope.mainContainerStyle = function () { if (newValue.w > 890) { return {}; } else { val = newValue.w/890; return { '-webkit-transform': 'scale(' + val + ')', '-o-transform': 'scale(' + val + ')', '-ms-transform': 'scale(' + val + ')', 'transform': 'scale(' + val + ')', 'transform-origin': 'left -10px', '-webkit-transform-origin': 'left -10px' }; } }; scope.topBarStyle = function () { if (newValue.w > 890) { return {}; } else { val = newValue.w/890; return { '-webkit-transform': 'scale(' + val + ')', '-o-transform': 'scale(' + val + ')', '-ms-transform': 'scale(' + val + ')', 'transform': 'scale(' + val + ')', 'transform-origin': '0 2px 0', '-webkit-transform-origin': '0 2px 0' }; } }; }, true); w.bind('resize', function () { scope.$apply(); }); } })

As you can see all the magic is done with transform:scale CSS attribute on the two of my main page components: the navigation and the contents container.

They styles are cross-browser.

return { '-webkit-transform': 'scale(' + val + ')', '-o-transform': 'scale(' + val + ')', '-ms-transform': 'scale(' + val + ')', 'transform': 'scale(' + val + ')' };

It’s important to set transform-origin, or the elements will be weirdly positioned on the page.

return { 'transform-origin': '0 top', '-webkit-transform-origin': '0 top' };

The style calculations are attached to the changes of window dimensions.

scope.getWindowDimensions = function () { return { 'h': $window.innerHeight, 'w': $window.innerWidth }; }; scope.$watch(scope.getWindowDimensions, function (newValue, oldValue) { ... });

Few other things. My layout was sliced to the fixed width of 890px, that’s why I took 890 as the pivotal point of my scale ratio formula. You should take the default width of the layout as the base of your calculation.

if (newValue.w > 890) { return {}; } else { val = newValue.w/890; return { '-webkit-transform': 'scale(' + val + ')', } });

With the directive in place it’s time to plug it in:

<script src="scripts/directives/tsResize.js"></script> <nav ng-style="topBarStyle()" ts-resize=""></nav> <div ng-style="mainContainerStyle()" ng-view="" ts-resize=""></div>

Be sure to use style “display:block” or “display:inline-block” and “position:relative” for all the inside components of the scaled elements with the default display. Otherwise they do not obey the scaling enforcement and grow way too long prompting a scrollbar.

<div id="main-container"><div id="inner-block" style="position:relative; display:inline-block"></div></div>

It all worked nicely and I was able to enjoy the smoothly resizing layout.

angular css design html javascript