Many 3rd party libraries exist for providing various functionality for Angular, but sometimes these libraries do not fit specific use cases that arise -- one such instance is using Angular Material's md-autocomplete directive.

Angular Material "is both a UI Component framework and a reference implementation of Google's Material Design Specification," which means the goal of Angular Material is to adhere to the Material Design specifications. So it would make sense that the design choices are tailored to adhering to this spec. That said, sometimes we want to take pieces and mold them to our own requiremnts, and fortunately Angular makes this entirely possible.

One such requirement is that I wanted a specific number of elements to be visible in the list of items displayed using md-autocomplete, however I did not want to modify all md-autocomplete directives used within our site. The md-autocomplete directive creates an md-virtual-repeat container to display the matching items, however in order to customize the container the only available CSS targeting options would be to modify all of the virtual containers that get created by the directive. So I began searching and eventually came across a pull request against the Angular Material repository which provided the functionality, but was closed for various reasons.

So that's it for the intro, now here's what we need to do in order to extend the existing md-autocomplete directive in order to allow the ability to specifiy a class for the md-virtual-repeat container which is created when displaying the autocomplete items.

Note: John Papa's style guidelines heavily influences our formatting, so that is why some aspects might be written the way they are.

Fortunately what needs to be done is short and sweet; We simply create a decorator in our app's config block to extend the original directive's scope (1.1.1 scope) to include an attribute called menuContainerClass (menu-container-class) then recompile the md-virtual-repeat-container template (created by this directive) to include the class set by this attribute (if it is present).

  (function() {
  angular.module('mdAutoCompleteDemo')
  .config(config);

  config.$inject = ['$provide'];

  // add additional functionality to md-autocomplete
  function config($provide) {
    // extend Angular Material's mdAutoCompleteDirective
    $provide.decorator(
      'mdAutocompleteDirective', 
      mdAutoCompleteDirectiveOverride
    );

    mdAutoCompleteDirectiveOverride.$inject = ['$delegate'];

    function mdAutoCompleteDirectiveOverride($delegate) {
      // grab the directive
      var directive = $delegate[0];

      // need to append to base compile function
      var compile = directive.compile;

      // add our custom attribute to the directive's scope
      angular.extend(directive.scope, {
        menuContainerClass: '@?mdMenuContainerClass'
      });

      // recompile directive and add our class to the virtual container
      directive.compile = function(element, attr) {
        var template = compile.apply(this, arguments);
        var menuContainerClass = 
          attr.mdMenuContainerClass ? attr.mdMenuContainerClass : '';
        var menuContainer = element.find('md-virtual-repeat-container');

        menuContainer.addClass(menuContainerClass);

        // recompile the template
        return function(e, a) {
          template.apply(this, arguments);
        };
      };

      return $delegate;
    }
  }
})();

Once this is set in our app's config block we can use the menu-container-class attribute in any md-autocomplete directive to set the class for that directive's related md-virtual-repeat container.

Below is an example with the class being set, and one with the default behavior (no class styling to the virtual repeat container). I am using !important when setting the max-height as I've found it to be necessary to override the default CSS.

Type in 'a' to see the most results

With the class

Without the class


916 0 0