HTML5 Zone is brought to you in partnership with:

Matt Raible has been building web applications for most of his adult life. He started tinkering with the web before Netscape 1.0 was even released. For the last 16 years, Matt has helped companies adopt open source technologies (Spring, Hibernate, Apache, Struts, Tapestry, Grails) and use them effectively. Matt has been a speaker at many conferences worldwide, including Devoxx, Jfokus, ÜberConf, No Fluff Just Stuff, and a host of others. Matt is a DZone MVB and is not an employee of DZone and has posted 149 posts at DZone. You can read more from them at their website. View Full User Profile

Developing with AngularJS - Part IV: Making it Pop

09.27.2013
| 17201 views |
  • submit to reddit

Welcome to the final article in a series on my experience developing with AngularJS. I learned its concepts, beat my head against-the-wall, and finally tamed it enough to create a "My Dashboard" feature for a client. For previous articles, please see the following:

The last mile of development for the My Dashboard feature was to spice things up a bit and make it look better. We hired a design company to come up with a new look and feel, and they went to work. Within a week, we had a meeting with them and they presented a few different options. We picked the one we liked the best and went to work. Below are screenshots that I used to implement the new design.

My Dashboard - New DesignMy Dashboard with Show More

At first, I thought implementing this design might take quite a bit of effort, since it looked like it used custom fonts. It's true we could use CSS3's @font-face, but I knew it might take awhile to find the right fonts with the appropriate licenses. When I received the screenshot below, I was pleased to see that all fonts were web-safe.

My Dashboard Fonts

Design Elements

There are a number of elements in this new design that I had to create. For example, if numbers were only one digit, we had to add a leading zero to them in the summary band. Other design elements we needed to implement are listed below:

  • A background image that filled the page
  • Fade to white on summary widget titles
  • A responsive grid for summary widgets
  • Provide a colored background for odd rows in the summary grid
  • Add a "Show More" band at the bottom of Tasks, Summary and Reports when there's more items to display

In addition to these elements, there was quite a bit of work to conform to the new colors, fonts and drop-shadows. I implemented all of these using CSS3 (border-radius, box-shadow, box-sizing, linear-gradient), and lots of trial-and-error. To use the best fonts across various devices, I used CSS-Trick's Font Stacks.

New Background

The new background shown in the screenshots above has a light source in the middle of it. Therefore, it's impossible to tile/repeat it across the page, because it's not uniform. To make it work, I used a 1024 x 768 image and CSS3's background-size: cover. For more information on background-size, see SitePoint's How to Resize Background Images with CSS3. This worked great on smaller screens, but we noticed some issues on 30" monitors. Therefore, we ended up getting a new repeatable background and stopped using background-size.

LeadingZero filter

For the first leading zero feature, I wrote an Angular filter. I put the code for this in filters.js:

filter('leadingZero', function() {
    return function(input) {
        if (input.length === 1) {
            return "0" + input;
        } else if (input.length > 2) {
            return "+99";
        } else {
            return input;
        }
    }
});

This filter is used in the HTML template as follows:

<div class="summary-value">{{widget.value | leadingZero}}</div>

Text Fade Out

To implement the fade-to-white text in summary titles, I started with this tutorial. I quickly discovered that it worked best for vertical text blocks and not for horizontal text. Then I found Text Ellipsis with Gradient Fade in Pure CSS, which uses :after to position a block over the text that fades to white. Since the title is not the right-most element (the numbers are), I had to figure out the best positioning that worked cross-browser. Below is the CSS I used to implement this feature:

.dashboard .summary-title:after {
    display: block;
    position: absolute;
    right: 66px;
    top: 5px;
    bottom: 5px;
    width: 30px;
    background: -moz-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* FF3.6+ */
    background: -webkit-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* Opera 11.10+ */
    background: -ms-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* IE10+ */
    background: linear-gradient(to right,  rgba(255,255,255,0) 0%, #fff 20px); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ffffff',GradientType=1 ); /* IE6-9 */
    content: "";
}

Responsive Grid

To implement the responsive grid of summary widgets, I started with Codrops' Responsive Full Width Grid Tutorial. This proved to be a great model and I used the following CSS to position all the <li>'s appropriately. In the code below, .summary-item is the class on the <li> elements.

.dashboard .summary-item {
    border-right: 1px solid #d1d1d1;
    border-bottom: 1px solid #d1d1d1;
    /* put the border on the inside of the box */
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    box-sizing: border-box;
    font-family: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
    font-size: 14px;
    color: #666;
    height: 50px;
    box-shadow: inset 0 0 6px rgba(0,0,0, 0.25);
    /* responsive grid */
    position: relative;
    float: left;
    overflow: hidden;
    width: 25% /* Fallback */
    width: -webkit-calc(100% / 4);
    width: calc(100% / 4);
}
 
@media screen and (max-width: 1400px) {
    .dashboard .summary-item {
        width: 33.33333333333333%; /* Fallback */
        width: -webkit-calc(100% / 3);
        width: calc(100% / 3);
    }
}
 
@media screen and (max-width: 1000px) {
    .dashboard .summary-item {
        width: 50%; /* Fallback */
        width: -webkit-calc(100% / 2);
        width: calc(100% / 2);
    }
}

This worked great in most browsers, but we did find an issue with IE9. When squishing or expanding the browser window, sometimes there would be a blank column on the right side. To fix this, I changed the width on the default .summary-item to be 25%, and removed the lines with calc.

.dashboard .summary-item {
    ...
    width: 25%
}

Coloring Odd Rows

Coloring odd rows in a table is easy, but when the rows are in a responsive grid, that's a whole different story. For tables, the CSS rules are extremely simple:

tr:nth-child(even) {background: #CCC}
tr:nth-child(odd) {background: #FFF}

Via Twitter, @tomaslin advised me that the nth-child selector could probably be used for this, but it'd likely require some JavaScript to make it responsive. I found the excellent Master of the :nth-child and began trying to figure it out. The following function is what we now use to color odd rows in the Summary Bar.

function colorRows() {
    var lisInRow = 0;
    var items = $('.summary-items li');
    items.each(function() {
        if($(this).prev().length > 0) {
            if($(this).position().top != $(this).prev().position().top) return false;
            lisInRow++;
        }
        else {
            lisInRow++;
        }
    });
    var rows = items.length / lisInRow;
    for (var i = 0; i < rows; i++) {
        var selector = "nth-child(n+{x}):nth-child(-n+{y})";
        var x = (lisInRow * i) + 1;
        var y = x + (lisInRow - 1);
        selector = selector.replace('{x}', '' + x);
        selector = selector.replace('{y}', '' + y);
        if (i % 2) {
            $('.summary-items li:' + selector).addClass('odd');
        } else {
            $('.summary-items li:' + selector).removeClass('odd');
        }
    }
}

The above code is in dashboard.js and is called anytime the browser window is resized (to adapt to the responsive grid).

$(window).resize(colorRows);

It's also called when summary widgets are re-ordered, in the updateOrder() function of WidgetController.

$scope.updateOrder = function(event, ui) {
    ...
    Preferences.saveWidgetOrder(type, {items: items});
    if (type === 'summary') {
        colorRows();
    }
};

I'd like to figure out how to make this more Angular-esque, but all the "how to hook into window.resize" articles I found make it seem harder than this.

Published at DZone with permission of Matt Raible, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)