• Home

  • Custom Ecommerce
  • Application Development
  • Database Consulting
  • Cloud Hosting
  • Systems Integration
  • Legacy Business Systems
  • Security & Compliance
  • GIS

  • Expertise

  • About Us
  • Our Team
  • Clients
  • Blog
  • Careers

  • CasePointer

  • VisionPort

  • Contact
  • Our Blog

    Ongoing observations by End Point Dev people

    Angular Responsive Layout Directive

    Marina Lohova

    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


    Comments