Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 1e4ba35

Browse files
crisbetokara
authored andcommitted
feat(menu): expose close method on element scope; deprecate $mdOpenMenu (#9193)
* Deprecates the `$mdOpenMenu` method in favor of `$mdMenu.open` in order to simplify the API. * Exposes the `$mdMenu.close` method on the menu's scope, allowing for custom closing behavior. Fixes #8446.
1 parent 9e0c30e commit 1e4ba35

File tree

15 files changed

+113
-58
lines changed

15 files changed

+113
-58
lines changed

src/components/list/list.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) {
295295
// When the proxy item is aligned at the end of the list, we have to set the origin to the end.
296296
xAxisPosition = 'right';
297297
}
298-
298+
299299
// Set the position mode / origin of the proxied menu.
300300
if (!menuEl.attr('md-position-mode')) {
301301
menuEl.attr('md-position-mode', xAxisPosition + ' target');
@@ -304,7 +304,7 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) {
304304
// Apply menu open binding to menu button
305305
var menuOpenButton = menuEl.children().eq(0);
306306
if (!hasClickEvent(menuOpenButton[0])) {
307-
menuOpenButton.attr('ng-click', '$mdOpenMenu($event)');
307+
menuOpenButton.attr('ng-click', '$mdMenu.open($event)');
308308
}
309309

310310
if (!menuOpenButton.attr('aria-label')) {

src/components/list/list.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ describe('mdListItem directive', function() {
294294

295295
// The actual click button will be a child of the button.md-no-style wrapper.
296296
var buttonExecutor = buttonWrap.children()[0];
297-
297+
298298
expect(buttonExecutor.nodeName).toBe('MD-BUTTON');
299299
expect(buttonExecutor.getAttribute('aria-label')).toBe('Hello');
300300
});
@@ -497,7 +497,7 @@ describe('mdListItem directive', function() {
497497

498498
var mdMenuButton = listItem[0].querySelector('md-menu > md-button');
499499

500-
expect(mdMenuButton.getAttribute('ng-click')).toBe('$mdOpenMenu($event)');
500+
expect(mdMenuButton.getAttribute('ng-click')).toBe('$mdMenu.open($event)');
501501
});
502502
});
503503

src/components/menu/demoBasicUsage/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ <h2 class="md-title">Simple dropdown menu</h2>
55
<p>Applying the <code>md-menu-origin</code> and <code>md-menu-align-target</code> attributes ensure that the menu elements align.
66
Note: If you select the Redial menu option, then a modal dialog will zoom out of the phone icon button.</p>
77
<md-menu>
8-
<md-button aria-label="Open phone interactions menu" class="md-icon-button" ng-click="ctrl.openMenu($mdOpenMenu, $event)">
8+
<md-button aria-label="Open phone interactions menu" class="md-icon-button" ng-click="ctrl.openMenu($mdMenu, $event)">
99
<md-icon md-menu-origin md-svg-icon="call:phone"></md-icon>
1010
</md-button>
1111
<md-menu-content width="4">

src/components/menu/demoBasicUsage/script.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ angular
88
.controller('BasicDemoCtrl', function DemoCtrl($mdDialog) {
99
var originatorEv;
1010

11-
this.openMenu = function($mdOpenMenu, ev) {
11+
this.openMenu = function($mdMenu, ev) {
1212
originatorEv = ev;
13-
$mdOpenMenu(ev);
13+
$mdMenu.open(ev);
1414
};
1515

1616
this.notificationsEnabled = true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<div class="md-menu-demo" ng-cloak>
2+
<div class="menu-demo-container" layout-align="center center" layout="column">
3+
<h2 class="md-title">Custom triggers</h2>
4+
<p>
5+
You can customize the events that open and close a menu by using the <code>$mdMenu.open</code> and
6+
<code>$mdMenu.close</code> methods. This is an example of triggering a menu on hover, instead of click.
7+
</p>
8+
9+
<md-menu>
10+
<md-button aria-label="Open menu with custom trigger" class="md-icon-button" ng-mouseenter="$mdMenu.open()">
11+
<md-icon md-menu-origin md-svg-icon="call:textsms"></md-icon>
12+
</md-button>
13+
<md-menu-content width="4" ng-mouseleave="$mdMenu.close()">
14+
<md-menu-item ng-repeat="item in [1, 2, 3]">
15+
<md-button>
16+
Option {{item}}
17+
</md-button>
18+
</md-menu-item>
19+
</md-menu>
20+
</div>
21+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
angular
2+
.module('menuDemoCustomTrigger', ['ngMaterial'])
3+
.config(function($mdIconProvider) {
4+
$mdIconProvider
5+
.iconSet('call', 'img/icons/sets/communication-icons.svg', 24);
6+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.md-menu-demo {
2+
padding: 24px;
3+
}
4+
5+
.menu-demo-container {
6+
min-height: 200px;
7+
}

src/components/menu/demoMenuPositionModes/index.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ <h2 class="md-title">Position Mode Demos</h2>
88
<div layout="column" flex-xs="100" flex-sm="100" flex="33" layout-align="center center">
99
<p>Target Mode Positioning (default)</p>
1010
<md-menu>
11-
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdOpenMenu($event)">
11+
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdMenu.open($event)">
1212
<md-icon md-menu-origin md-svg-icon="call:business"></md-icon>
1313
</md-button>
1414
<md-menu-content width="6">
1515
<md-menu-item ng-repeat="item in [1, 2, 3]">
1616
<md-button ng-click="ctrl.announceClick($index)">
1717
<md-icon md-menu-align-target md-svg-icon="call:no-sim"></md-icon>
18-
Option {{item}}
18+
Option {{item}}
1919
</md-button>
2020
</md-menu-item>
2121
</md-menu-content>
@@ -24,7 +24,7 @@ <h2 class="md-title">Position Mode Demos</h2>
2424
<div layout="column" flex-xs="100" flex-sm="100" flex="33" layout-align="center center">
2525
<p>Target mode with <code>md-offset</code></p>
2626
<md-menu md-offset="0 -5">
27-
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="ctrl.openMenu($mdOpenMenu, $event)">
27+
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="ctrl.openMenu($mdMenu, $event)">
2828
<md-icon md-menu-origin md-svg-icon="call:ring-volume"></md-icon>
2929
</md-button>
3030
<md-menu-content width="4">
@@ -37,7 +37,7 @@ <h2 class="md-title">Position Mode Demos</h2>
3737
<div layout="column" flex-xs="100" flex-sm="100" flex="33" layout-align="center center">
3838
<p><code>md-position-mode="target-right target"</code></p>
3939
<md-menu md-position-mode="target-right target" >
40-
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdOpenMenu($event)">
40+
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdMenu.open($event)">
4141
<md-icon md-menu-origin md-svg-icon="call:portable-wifi-off"></md-icon>
4242
</md-button>
4343
<md-menu-content width="4" >

src/components/menu/demoMenuPositionModes/script.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ angular
77
})
88
.controller('PositionDemoCtrl', function DemoCtrl($mdDialog) {
99
var originatorEv;
10-
10+
1111
this.menuHref = "http://www.google.com/design/spec/components/menus.html#menus-specs";
1212

13-
this.openMenu = function($mdOpenMenu, ev) {
13+
this.openMenu = function($mdMenu, ev) {
1414
originatorEv = ev;
15-
$mdOpenMenu(ev);
15+
$mdMenu.open(ev);
1616
};
1717

1818
this.announceClick = function(index) {

src/components/menu/demoMenuWidth/index.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ <h2 class="md-title">Different Widths</h2>
1111
<div layout="column" flex="33" flex-sm="100" flex-xs="100" layout-align="center center">
1212
<p>Wide menu (<code>width=6</code>)</p>
1313
<md-menu md-offset="0 -7">
14-
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdOpenMenu($event)">
14+
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdMenu.open($event)">
1515
<md-icon md-menu-origin md-svg-icon="call:phone"></md-icon>
1616
</md-button>
1717
<md-menu-content width="6">
@@ -24,7 +24,7 @@ <h2 class="md-title">Different Widths</h2>
2424
<div layout="column" flex="33" flex-sm="100" flex-xs="100" layout-align="center center">
2525
<p>Medium menu (<code>width=4</code>)</p>
2626
<md-menu md-offset="0 -7">
27-
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdOpenMenu($event)">
27+
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdMenu.open($event)">
2828
<md-icon md-menu-origin md-svg-icon="call:email"></md-icon>
2929
</md-button>
3030
<md-menu-content width="4">
@@ -37,7 +37,7 @@ <h2 class="md-title">Different Widths</h2>
3737
<div layout="column" flex="33" flex-sm="100" flex-xs="100" layout-align="center center">
3838
<p>Small menu (<code>width=2</code>)</p>
3939
<md-menu md-offset="0 -7">
40-
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdOpenMenu($event)">
40+
<md-button aria-label="Open demo menu" class="md-icon-button" ng-click="$mdMenu.open($event)">
4141
<md-icon md-menu-origin md-svg-icon="call:chat"></md-icon>
4242
</md-button>
4343
<md-menu-content width="2">

src/components/menu/js/menuController.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ angular
77
/**
88
* @ngInject
99
*/
10-
function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $rootScope, $q) {
10+
function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $rootScope, $q, $log) {
1111

1212
var prefixer = $mdUtil.prefixer();
1313
var menuContainer;
@@ -141,9 +141,6 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
141141
});
142142
};
143143

144-
// Expose a open function to the child scope for html to use
145-
$scope.$mdOpenMenu = this.open;
146-
147144
this.onIsOpenChanged = function(isOpen) {
148145
if (isOpen) {
149146
menuContainer.attr('aria-hidden', 'false');
@@ -234,4 +231,16 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
234231
throw Error('Invalid offsets specified. Please follow format <x, y> or <n>');
235232
}
236233
};
234+
235+
// Functionality that is exposed in the view.
236+
$scope.$mdMenu = {
237+
open: this.open,
238+
close: this.close
239+
};
240+
241+
// Deprecated APIs
242+
$scope.$mdOpenMenu = angular.bind(this, function() {
243+
$log.warn('mdMenu: The $mdOpenMenu method is deprecated. Please use `$mdMenu.open`.');
244+
return this.open.apply(this, arguments);
245+
});
237246
}

src/components/menu/js/menuDirective.js

+16-13
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
*
1111
* Every `md-menu` must specify exactly two child elements. The first element is what is
1212
* left in the DOM and is used to open the menu. This element is called the trigger element.
13-
* The trigger element's scope has access to `$mdOpenMenu($event)`
13+
* The trigger element's scope has access to `$mdMenu.open($event)`
1414
* which it may call to open the menu. By passing $event as argument, the
15-
* corresponding event is stopped from propagating up the DOM-tree.
15+
* corresponding event is stopped from propagating up the DOM-tree. Similarly, `$mdMenu.close()`
16+
* can be used to close the menu.
1617
*
1718
* The second element is the `md-menu-content` element which represents the
1819
* contents of the menu when it is open. Typically this will contain `md-menu-item`s,
@@ -21,7 +22,7 @@
2122
* <hljs lang="html">
2223
* <md-menu>
2324
* <!-- Trigger element is a md-button with an icon -->
24-
* <md-button ng-click="$mdOpenMenu($event)" class="md-icon-button" aria-label="Open sample menu">
25+
* <md-button ng-click="$mdMenu.open($event)" class="md-icon-button" aria-label="Open sample menu">
2526
* <md-icon md-svg-icon="call:phone"></md-icon>
2627
* </md-button>
2728
* <md-menu-content>
@@ -63,7 +64,7 @@
6364
*
6465
* <hljs lang="html">
6566
* <md-menu>
66-
* <md-button ng-click="$mdOpenMenu($event)" class="md-icon-button" aria-label="Open some menu">
67+
* <md-button ng-click="$mdMenu.open($event)" class="md-icon-button" aria-label="Open some menu">
6768
* <md-icon md-menu-origin md-svg-icon="call:phone"></md-icon>
6869
* </md-button>
6970
* <md-menu-content>
@@ -123,22 +124,24 @@
123124
* Sometimes you would like to be able to click on a menu item without having the menu
124125
* close. To do this, ngMaterial exposes the `md-prevent-menu-close` attribute which
125126
* can be added to a button inside a menu to stop the menu from automatically closing.
126-
* You can then close the menu programatically by injecting `$mdMenu` and calling
127-
* `$mdMenu.hide()`.
127+
* You can then close the menu either by using `$mdMenu.close()` in the template,
128+
* or programatically by injecting `$mdMenu` and calling `$mdMenu.hide()`.
128129
*
129130
* <hljs lang="html">
130-
* <md-menu-item>
131-
* <md-button ng-click="doSomething()" aria-label="Do something" md-prevent-menu-close="md-prevent-menu-close">
132-
* <md-icon md-menu-align-target md-svg-icon="call:phone"></md-icon>
133-
* Do Something
134-
* </md-button>
135-
* </md-menu-item>
131+
* <md-menu-content ng-mouseleave="$mdMenu.close()">
132+
* <md-menu-item>
133+
* <md-button ng-click="doSomething()" aria-label="Do something" md-prevent-menu-close="md-prevent-menu-close">
134+
* <md-icon md-menu-align-target md-svg-icon="call:phone"></md-icon>
135+
* Do Something
136+
* </md-button>
137+
* </md-menu-item>
138+
* </md-menu-content>
136139
* </hljs>
137140
*
138141
* @usage
139142
* <hljs lang="html">
140143
* <md-menu>
141-
* <md-button ng-click="$mdOpenMenu($event)" class="md-icon-button">
144+
* <md-button ng-click="$mdMenu.open($event)" class="md-icon-button">
142145
* <md-icon md-svg-icon="call:phone"></md-icon>
143146
* </md-button>
144147
* <md-menu-content>

src/components/menu/menu.spec.js

+23-14
Original file line numberDiff line numberDiff line change
@@ -66,25 +66,23 @@ describe('material.components.menu', function() {
6666
});
6767

6868
it('opens on click without $event', function() {
69-
var noEvent = true;
70-
var menu = setup('ng-click', noEvent);
69+
var menu = setup('ng-click="$mdMenu.open()"');
7170
openMenu(menu);
7271
expect(getOpenMenuContainer(menu).length).toBe(1);
7372
closeMenu(menu);
7473
expect(getOpenMenuContainer(menu).length).toBe(0);
7574
});
7675

7776
it('opens on mouseEnter', function() {
78-
var menu = setup('ng-mouseenter');
77+
var menu = setup('ng-mouseenter="$mdMenu.open($event)"');
7978
openMenu(menu, 'mouseenter');
8079
expect(getOpenMenuContainer(menu).length).toBe(1);
8180
closeMenu(menu);
8281
expect(getOpenMenuContainer(menu).length).toBe(0);
8382
});
8483

8584
it('opens on mouseEnter without $event', function() {
86-
var noEvent = true;
87-
var menu = setup('ng-mouseenter', noEvent);
85+
var menu = setup('ng-mouseenter="$mdMenu.open()"');
8886
openMenu(menu, 'mouseenter');
8987
expect(getOpenMenuContainer(menu).length).toBe(1);
9088
closeMenu(menu);
@@ -116,7 +114,7 @@ describe('material.components.menu', function() {
116114

117115
it('should remove the backdrop if the container scope got destroyed', inject(function($document, $rootScope) {
118116
var scope = $rootScope.$new();
119-
var menu = setup(null, null, scope);
117+
var menu = setup(null, scope);
120118

121119
openMenu(menu);
122120
expect($document.find('md-backdrop').length).not.toBe(0);
@@ -203,7 +201,7 @@ describe('material.components.menu', function() {
203201
it('should focus a button with md-menu-focus-target', inject(function($compile, $rootScope, $document) {
204202
var menu = $compile(
205203
'<md-menu>' +
206-
'<button ng-click="$mdOpenMenu($event)">Hello World</button>' +
204+
'<button ng-click="$mdMenu.open($event)">Hello World</button>' +
207205
'<md-menu-content>' +
208206
'<md-menu-item>' +
209207
'<button ng-click="doSomething($event)"></button>' +
@@ -225,7 +223,7 @@ describe('material.components.menu', function() {
225223
it('should focus a button with md-autofocus', inject(function($compile, $rootScope, $document) {
226224
var menu = $compile(
227225
'<md-menu>' +
228-
'<button ng-click="$mdOpenMenu($event)">Hello World</button>' +
226+
'<button ng-click="$mdMenu.open($event)">Hello World</button>' +
229227
'<md-menu-content>' +
230228
'<md-menu-item>' +
231229
'<button ng-click="doSomething($event)"></button>' +
@@ -266,6 +264,18 @@ describe('material.components.menu', function() {
266264
expect(getOpenMenuContainer(menu).length).toBe(0);
267265
});
268266

267+
it('closes via the scope method', function() {
268+
var menu = setup('ng-mouseenter="$mdMenu.open($event)" ng-mouseleave="$mdMenu.close()"');
269+
270+
expect(getOpenMenuContainer(menu).length).toBe(0);
271+
openMenu(menu, 'mouseenter');
272+
expect(getOpenMenuContainer(menu).length).toBe(1);
273+
274+
menu.find('button').triggerHandler('mouseleave');
275+
waitForMenuClose();
276+
expect(getOpenMenuContainer(menu).length).toBe(0);
277+
});
278+
269279
itClosesWithAttributes([
270280
'data-ng-click', 'x-ng-click',
271281
'ui-sref', 'data-ui-sref', 'x-ui-sref',
@@ -278,10 +288,10 @@ describe('material.components.menu', function() {
278288
}
279289

280290
function testAttribute(attr) {
281-
return inject(function($rootScope, $compile, $timeout, $browser) {
291+
return inject(function($rootScope, $compile, $timeout) {
282292
var template = '' +
283293
'<md-menu>' +
284-
' <button ng-click="$mdOpenMenu($event)">Hello World</button>' +
294+
' <button ng-click="$mdMenu.open($event)">Hello World</button>' +
285295
' <md-menu-content>' +
286296
' <md-menu-item>' +
287297
' <md-button ' + attr + '=""></md-button>' +
@@ -307,17 +317,17 @@ describe('material.components.menu', function() {
307317
}
308318
});
309319

310-
function setup(triggerType, noEvent, scope) {
320+
function setup(buttonAttrs, scope) {
311321
var menu,
312322
template = $mdUtil.supplant('' +
313323
'<md-menu>' +
314-
' <button {0}="$mdOpenMenu({1})">Hello World</button>' +
324+
' <button {0}>Hello World</button>' +
315325
' <md-menu-content>' +
316326
' <md-menu-item>' +
317327
' <md-button ng-click="doSomething($event)"></md-button>' +
318328
' </md-menu-item>' +
319329
' </md-menu-content>' +
320-
'</md-menu>',[ triggerType || 'ng-click', noEvent ? '' : "$event" ]);
330+
'</md-menu>', [ buttonAttrs || 'ng-click="$mdMenu.open($event)"' ]);
321331

322332
inject(function($compile, $rootScope) {
323333
$rootScope.doSomething = function($event) {
@@ -357,7 +367,6 @@ describe('material.components.menu', function() {
357367

358368
function closeMenu() {
359369
inject(function($document) {
360-
$document.find('md-backdrop');
361370
$document.find('md-backdrop').triggerHandler('click');
362371
waitForMenuClose();
363372
});

0 commit comments

Comments
 (0)