Creating a MySpace Style Full Screen Search Using Angular Directives

MySpace (yeah it still exists!) has a really cool UI search feature. Just start typing and a slick full screen search interface pops up. For a recent project we were building in Angular, we wanted to implement something similar. Sounds like the perfect place for some directives!

To create this feature, we're going to create two directives.

The first, keyPressEvents will be work as a listener that will subscribe to all keypress events on the document and will use $rootScope to broadcast those events to any other directives that care to listen.

The second, search, will subscribe to the $rootScope keypress events and will pull up a search interface. It will also listen for specific keys, enter and esc to perform a search and to exit respectively.

Capturing Keypress Events

Our first directive will be a general purpose event listener that will listen for any keypress events on the document, and will then use $rootScope to broadcast them throughout the application.

angular.module('recipes.keypress', [])  
  .directive('keypressEvents', [
    '$document',
    '$rootScope',
    function($document, $rootScope) {
      return {
        restrict: 'A',
        link: function() {
          $document.bind('keydown', function(e) {
            $rootScope.$broadcast('keypress', e);
            $rootScope.$broadcast('keypress:' + e.which, e);
          });
        }
      };
    }
  ]);

On each keypress, this directive will broadcast two events on $rootScope. The first is keypress which will pass the keypress event as a parameter. The second, keypress:<pressed key>, allows us to easily listen for specific keys being pressed.

Attach the keypressEvents as an attribute on the body element in your index.html file.

<body keypress-events>  
  //Awesome Angular App
</body>  

Full Screen Search Directive

The search directive is where most of the UI goodness will happen. This directive will listen for the keypress events broadcast on $rootScope the from keypressEvents directive and will display a fullscreen search element capturing the user's query.

It will listen for the enter key to execute the search. It will use $rootScope to broadcast the search event and pass the query string as a parameter to any listeners you want to set up to deal process the search.

It will also listen for esc to hide itself.

In case you want to disable it, like you need to catch user input without the search popping up and ruining everything, we will create another $rootScope variable, search which by default is set to true. If keyboard input is required outside of the search, the search can be disabled by setting $rootScope.search = false.

angular.module('recipes.search', [])  
  .directive('search', ['$rootScope', '$state', function($rootScope, $state) {
    return {
      restrict: 'E',
      replace: true,
      templateUrl: 'app/directives/search/search.html',
      controller: 'SearchCtrl',
      link: function(scope, el, attr) {

        // Default search to true
        $rootScope.search = true;
        var searchbar = el.find('#searchtext');

        // Subscribe to $rootScope events from keypressEvents directive
        scope.$on('keypress', function(onEvent, keypressEvent) {
          // Disable searching if $rootScope.search is set to false anywhere in the app
          if ($rootScope.search) {

            // On escape
            if (keypressEvent.which === 27) {
              searchbar.val('').blur();
              el.fadeOut(200);
              // On enter
            } else if (keypressEvent.which === 13) {
              // Broadcast the search event on enter, clear the search input, and fade the search screen

              $rootScope.$broadcast('search', searchbar.val());
              searchbar.val('').blur();
              el.fadeOut(200);

            } else {
              if(!el.is(':visible')){
// Add in the first key the user presses              searchbar.val(String.fromCharCode(keypressEvent.which));
              }
              // Show the search input
              scope.key = String.fromCharCode(keypressEvent.which);
              searchbar.focus();
              el.fadeIn(200);
            }
          }
        });
      }
    };
  }]);

The search bar doesn't actually do anything with the search other than broadcast it out into app-land, so to add some functionality you need to subscribe to it in any of your other directives or controllers. To do so, make sure and inject the $rootScope dependency Then add in the following subscription.

$rootScope.$on('search', function(event, query){
  // Process Search
});

With this include a template html file.

<div id="search">  
    <form ng-model="searchCriteria" id="searchform">
        <label id="label" for="searchtext">search: </label>
        <input id="searchtext" name="searchtext" type="text" />
    </form>
</div>  

You need to add some CSS, which I won't go into here, to make it full screen.

Add in the <search></search> element on any page and you're good to go!