diff --git a/src/components/sidenav/sidenav.js b/src/components/sidenav/sidenav.js
index d18b60d5c3f..d4a64d265b1 100644
--- a/src/components/sidenav/sidenav.js
+++ b/src/components/sidenav/sidenav.js
@@ -29,44 +29,71 @@ angular.module('material.components.sidenav', [
*
* @usage
*
- * // Toggle the given sidenav
- * $mdSidenav(componentId).toggle();
+ * // Async lookup for sidenav instance; will resolve when the instance is available
+ * $mdSidenav(componentId).then(function(instance) {
+ * $log.debug( componentId + "is now ready" );
+ * });
+ *
+ *
+ * // Async toggle the given sidenav;
+ * // when instance is known ready and lazy lookup is not needed.
+ * $mdSidenav(componentId)
+ * .toggle()
+ * .then(function(){
+ * $log.debug('toggled');
+ * });
*
*
- * // Open the given sidenav
- * $mdSidenav(componentId).open();
+ * // Async open the given sidenav
+ * $mdSidenav(componentId)
+ * .open();
+ * .then(function(){
+ * $log.debug('opened');
+ * });
*
*
- * // Close the given sidenav
- * $mdSidenav(componentId).close();
+ * // Async close the given sidenav
+ * $mdSidenav(componentId)
+ * .close();
+ * .then(function(){
+ * $log.debug('closed');
+ * });
*
*
- * // Exposes whether given sidenav is set to be open
+ * // Sync check to see if the specified sidenav is set to be open
* $mdSidenav(componentId).isOpen();
*
*
- * // Exposes whether given sidenav is locked open
- * // If this is true, the sidenav will be open regardless of isOpen()
+ * // Sync check to whether given sidenav is locked open
+ * // If this is true, the sidenav will be open regardless of close()
* $mdSidenav(componentId).isLockedOpen();
*
*/
function SidenavService($mdComponentRegistry, $q) {
return function(handle) {
- var errorMsg = "SideNav '" + handle + "' is not available!";
// Lookup the controller instance for the specified sidNav instance
+ var self;
+ var errorMsg = "SideNav '" + handle + "' is not available!";
var instance = $mdComponentRegistry.get(handle);
+
if(!instance) {
$mdComponentRegistry.notFoundError(handle);
}
- return {
+ return self = {
+ // -----------------
+ // Sync methods
+ // -----------------
isOpen: function() {
return instance && instance.isOpen();
},
isLockedOpen: function() {
return instance && instance.isLockedOpen();
},
+ // -----------------
+ // Async methods
+ // -----------------
toggle: function() {
return instance ? instance.toggle() : $q.reject(errorMsg);
},
@@ -75,8 +102,24 @@ function SidenavService($mdComponentRegistry, $q) {
},
close: function() {
return instance ? instance.close() : $q.reject(errorMsg);
+ },
+ then : function( callbackFn ) {
+ var promise = instance ? $q.when(instance) : waitForInstance();
+ return promise.then( callbackFn || angular.noop );
}
};
+
+ /**
+ * Deferred lookup of component instance using $component registry
+ */
+ function waitForInstance() {
+ return $mdComponentRegistry
+ .when(handle)
+ .then(function( it ){
+ instance = it;
+ return it;
+ });
+ }
};
}
@@ -162,7 +205,10 @@ function SidenavDirective($timeout, $animate, $parse, $log, $mdMedia, $mdConstan
var isLockedOpenParsed = $parse(attr.mdIsLockedOpen);
var isLocked = function() {
return isLockedOpenParsed(scope.$parent, {
- $media: function(arg) { $log.warn("$media is deprecated for is-locked-open. Use $mdMedia instead."); return $mdMedia(arg); },
+ $media: function(arg) {
+ $log.warn("$media is deprecated for is-locked-open. Use $mdMedia instead.");
+ return $mdMedia(arg);
+ },
$mdMedia: $mdMedia
});
};
@@ -311,13 +357,17 @@ function SidenavController($scope, $element, $attrs, $mdComponentRegistry, $q) {
// Use Default internal method until overridden by directive postLink
- self.$toggleOpen = function() { return $q.when($scope.isOpen); };
+ // Synchronous getters
self.isOpen = function() { return !!$scope.isOpen; };
self.isLockedOpen = function() { return !!$scope.isLockedOpen; };
+
+ // Async actions
self.open = function() { return self.$toggleOpen( true ); };
self.close = function() { return self.$toggleOpen( false ); };
self.toggle = function() { return self.$toggleOpen( !$scope.isOpen ); };
+ self.$toggleOpen = function() { return $q.when($scope.isOpen); };
+
self.destroy = $mdComponentRegistry.register(self, $attrs.mdComponentId);
}
diff --git a/src/components/sidenav/sidenav.spec.js b/src/components/sidenav/sidenav.spec.js
index be4b7f01e0c..6ff74f46609 100644
--- a/src/components/sidenav/sidenav.spec.js
+++ b/src/components/sidenav/sidenav.spec.js
@@ -205,6 +205,15 @@ describe('mdSidenav', function() {
});
describe('$mdSidenav Service', function() {
+ var $rootScope, $timeout;
+
+
+ beforeEach( inject(function(_$rootScope_,_$timeout_) {
+ $rootScope = _$rootScope_;
+ $timeout = _$timeout_;
+ }));
+
+
it('should grab instance', inject(function($mdSidenav) {
var el = setup('md-component-id="left"');
var scope = el.isolateScope();
@@ -253,6 +262,28 @@ describe('mdSidenav', function() {
expect(instance.isOpen()).toBe(false);
expect(instance.isLockedOpen()).toBe(true);
}));
+
+ it('should find a deferred instantiation', inject(function($mdSidenav) {
+ var instance;
+
+ // Lookup deferred (not existing) instance
+ $mdSidenav('left').then( function(inst) { instance = inst; });
+ expect(instance).toBeUndefined();
+
+ // Instantiate `left` sidenav component
+ var el = setup('md-component-id="left"');
+
+ $timeout.flush();
+ expect(instance).toBeTruthy();
+ expect(instance.isOpen()).toBeFalsy();
+
+ // Lookup instance still available in the component registry
+ instance = undefined;
+ instance = $mdSidenav('left');
+
+ expect(instance).toBeTruthy();
+
+ }));
});
});