Building a sliding modal dialog with Angular JS

2 CH Chris Harrington Oct 9, 2014

There's no need to keep using a third-party modal plugin if you're wielding AngularJS. It's about time to build your own, and I'll show you how to build a customizable one that slides in from the top of the screen, while simultaneously fading in an overlay. The modal dialog itself is an Angular directive, and I'm using LESS for some of the slightly more complicated CSS rules. I'm going to assume you're relatively proficient with both, but if you're not, I highly recommend you check them out before plunging into this tutorial.

If you're the sort of reader who just wants to see the whole thing in action, feel free to take a look at the demo page I've put together that shows off the whole thing. Viewing the source on that page should give you a pretty good overview of what's going on.

Ok, time for some code. Here's the modal directive's HTML. It's pretty straightforward.

<div class="overlay" ng-class="{ show: visible, hide: !visible }"></div>
<div class="content" ng-class="{ show: visible }" ng-transclude></div>

We're using a couple of different CSS classes to perform the animations for sliding the modal in and out, and also for fading the overlay in and out. Here's the LESS CSS:

    modal { position:absolute; display:block; top:0; left:0; right:0;
        @buffer:75px;
        @animation-speed:250ms;
        >div.overlay { background:black; position:fixed; opacity:0.6; top:0; right:0; bottom:0; left:0; z-index:2;
                &.show { visibility:visible; opacity:0.6; transition:opacity ease @animation-speed; }
                &.hide { visibility:hidden; opacity:0; transition:visibility 0s @animation-speed, opacity ease @animation-speed; }
        }
        >div.content { position:absolute; left:0; right:0; margin:auto; background:white; border-radius:3px; width:500px; padding:15px; z-index:3; top:@buffer; transition:transform ease @animation-speed; transform:translate3d(0, ~"calc(-100% - 75px)", 0);
                &.show { .transform(translate3d(0, 0, 0)); }

                /* inner modal content styles go here! */
        }
}

I've left out the vendor-specific prefixes for the sake of brevity. Most of the CSS here is used for positioning and some basic styling. I've tried to keep the CSS as brief as possible so as to highlight the more important rules. I also feel as though this snippet really emphasizes the importance of using a CSS framework like LESS; it allows us to add variables and specify rules in a much more concise manner.

The animations for the overlay are specified in the show and hide classes, while the sliding for the modal itself is laid out in the show class. There are two rules for the overlay so as to facilitate the fading in as well as the fading out while setting the visibility. If we didn't do it this way, we'd only get one or the other, or have an overlay that's visible but transparent, preventing our users from interacting with the rest of the page.

The only other fancy code here is the transform:translate3d(0, ~"calc(-100% - 75px)", 0); line. This is using CSS3's calc function, which allows us to calculate size metrics on the fly. Very handy. Here, I'm saying that I want the modal to shift upward by it's full height, plus 75 pixels, which just so happens to be the buffer that separates it from the upper border of the document when it's shown.

That's it for the CSS! Now, here's the entirety of the JavaScript code for the Angular JS app:

var app = angular.module("demo", []).controller("modalDemo", function($scope, $rootScope) {
    $scope.visible = false;

    $scope.close = function() {
            $scope.visible = false;
    };

    document.addEventListener("keyup", function(e) {
            if (e.keyCode === 27)
                    $scope.$apply(function() {
                            if ($scope.visible === true)
                                    $scope.close();
                            });
            });
    }
}).directive("modal", function($rootScope, $timeout) {
    return {
            restrict: "E",
            templateUrl: "templates/modal.html",
            transclude: true
    };
});

The controller for the page defines a visible scope variable which is used to set the various CSS classes on the modal's HTML. We're hooking into the keyup document event so that we can listen for an escape keypress and, when we get one, broadcast an event that each modal will listen to and then hide the modal. And that's it!

The animations that this modal uses are one of the four that work well on mobile (opacity, transform:rotate, transform:scale and transform:translate), so it looks smooth on mobile devices, too. As I mentioned above, there's a demo that you can check out which shows the modal in all its glory.

Thanks for reading!

2 comments


Or enter your name and Email
  • K kishore 3 months ago
    good
  • K kool 2 years ago
    Hi, This is great example. I want to try same functionality without using less.min.js..Is it possible?