Igor Simic
6 years ago

Angular Material - how to make custom elements sticky using $mdSticky


To make any html element sticky, like for example sidebar, we can use $mdSticky service in Angular Material.This service is not well documented in official documentation regarding the elements structure, but nevertheless you can find two examples there:
- using $mdSticky for "non-compliled" html elements (elements which are not compiled by angular)
- using $mdSticky for angular compiled html elements (for example HTML elements loaded and rendered in ui-view)

In first case we can use $mdSticky like this:
HTML
<md-content ng-app="example" ng-controller="Main" layout-fill>
  <div sticky>
    <h2>This should be sticky</h2>
  </div>
  <div>
    <div ng-repeat="item in items">{{item.name}}</div>
  </div>
</md-content>
Angular
(function() {
  angular.module('example', ['ngMaterial']);
})();

 (function() {
  angular.module('example').directive('sticky', Sticky);

	Sticky.$inject = [ '$mdSticky' ];

	function Sticky($mdSticky) {
		return {
			restrict : 'A',
			link : function(scope, element) {
				$mdSticky(scope, element);
			}
		}
	}
})();

(function() {
  angular.module('example').controller('Main', Main);
  
  Main.$inject = ['$scope'];
  
  function Main($scope) {
    var items = [];
    for(var i = 0; i < 200; i++) {
      items.push({name: i.toString()});
    }
    $scope.items = items;
  }
})();
here is the codepen for this example created by Ken Long


Second example is little bit more complicated, so let's say that we want to make sidebar sticky, and this sidebar is loaded inside ui-view. So first we have to create directive:
app.directive('sticky', function($mdSticky, $compile) {
    var SELECT_TEMPLATE =
      '<md-content><md-card layout="column"> <md-card-content> <h2>Card headline</h2> <p>Card content</p> </md-card-content> <md-card-footer> Card footer </md-card-footer> <div class="md-actions" layout="column"> <md-button>content</md-button> </div> </md-card> <md-card layout="column"> <md-card-content> <h2>Card headline</h2> <p>Card content</p> </md-card-content> <md-card-footer> Card footer </md-card-footer> <div class="md-actions" layout="column"> <md-button>content</md-button> </div> </md-card> </md-content>';
    return {
      restrict: 'A',
      replace: true,
      //template: SELECT_TEMPLATE, // you can use template HTML or load HTML with templateURL as we do it in next line
      templateUrl:appInfo.template_directory+'templates/sticky-sidebar.directive.html',
      link: function(scope,element) {
        $mdSticky(scope, element, $compile(SELECT_TEMPLATE)(scope));
      }
    };
  });


in our example sticky-sidebar.directive.html is looking like this:
<md-content layout="column"  style="width: 400px;">

  <md-card> 
    <md-card-content>
      <h2>Card headline</h2>
      <p>Card content</p>
    </md-card-content>
    <md-card-footer>
      Card footer
    </md-card-footer>
    <div class="md-actions" layout="column">
      <md-button>content</md-button>
    </div>
  </md-card>

  <md-card> 
    <md-card-content>
      <h2>Card headline</h2>
      <p>Card content</p>
    </md-card-content>
    <md-card-footer>
      Card footer
    </md-card-footer>
    <div class="md-actions" layout="column">
      <md-button>content</md-button>
    </div>
  </md-card>
  <md-card> 
    <md-card-content>
      <h2>Card headline</h2>
      <p>Card content</p>
    </md-card-content>
    <md-card-footer>
      Card footer
    </md-card-footer>
    <div class="md-actions" layout="column">
      <md-button>content</md-button>
    </div>
  </md-card>

  <md-card> 
    <md-card-content>
      <h2>Card headline</h2>
      <p>Card content</p>
    </md-card-content>
    <md-card-footer>
      Card footer
    </md-card-footer>
    <div class="md-actions" layout="column">
      <md-button>content</md-button>
    </div>
  </md-card>

</md-content>

and to use it, it is very important that sticky element is a wrapped around with layout="row" and contains elemnt layout-fill or layout-align="start start"
<div layout="row"> 
  		 
	<md-content flex="100">

		<md-card>
			 
			<md-card-content>
				<h2>Card headline</h2>
				<p>Card content</p>
	 
		    	<p>
		    		Lorem250 / some very long text 
		    	</p>
	    	</md-card-content>
			<md-card-footer>
			Card footer
			</md-card-footer>

	    </md-card>

	</md-content>

	 
	<div sticky flex="nogrow" layout-fill></div>

</div>
Take a look at the end result on youtube:
https://youtu.be/yAty7caZong