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

Commit 9b861fd

Browse files
topherfangioThomasBurleson
authored andcommitted
fix(mdToolbar): Allow md-scroll-shrink usage with ng-if.
Previously, the toolbar would fail to enable scroll shrinking if the developer used an `ng-if` statement on the `md-toolbar`. This commit allows usage of `ng-if` as well as watching the `md-scroll-shrink` for changes so they can enable/disable scroll shrinking. Fixes #2751. Closes #4394.
1 parent 44a7e52 commit 9b861fd

File tree

2 files changed

+162
-23
lines changed

2 files changed

+162
-23
lines changed

src/components/toolbar/toolbar.js

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,22 @@ angular.module('material.components.toolbar', [
5656
* at one fourth the rate at which the user scrolls down. Default 0.5.
5757
*/
5858

59-
function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate ) {
60-
var translateY = angular.bind(null, $mdUtil.supplant, 'translate3d(0,{0}px,0)' );
59+
function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate) {
60+
var translateY = angular.bind(null, $mdUtil.supplant, 'translate3d(0,{0}px,0)');
6161

6262
return {
6363
restrict: 'E',
64+
template: '<div ng-transclude></div>',
65+
transclude: true,
6466
controller: angular.noop,
67+
scope: {
68+
scrollShrink: '=?mdScrollShrink'
69+
},
6570
link: function(scope, element, attr) {
6671

6772
$mdTheming(element);
6873

69-
if (angular.isDefined(attr.mdScrollShrink)) {
70-
setupScrollShrink();
71-
}
74+
setupScrollShrink();
7275

7376
function setupScrollShrink() {
7477
// Current "y" position of scroll
@@ -84,24 +87,68 @@ function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate )
8487
var debouncedContentScroll = $$rAF.throttle(onContentScroll);
8588
var debouncedUpdateHeight = $mdUtil.debounce(updateToolbarHeight, 5 * 1000);
8689

90+
var disableScrollShrink;
91+
8792
// Wait for $mdContentLoaded event from mdContent directive.
8893
// If the mdContent element is a sibling of our toolbar, hook it up
8994
// to scroll events.
9095
scope.$on('$mdContentLoaded', onMdContentLoad);
9196

97+
// If the toolbar is used inside an ng-if statement, we may miss the
98+
// $mdContentLoaded event, so we attempt to fake it if we have a
99+
// md-content close enough.
100+
scope.$watch('scrollShrink', onChangeScrollShrink);
101+
102+
// If the scope is destroyed (which could happen with ng-if), make sure
103+
// to disable scroll shrinking again
104+
scope.$on('$destroy', function() {
105+
disableScrollShrink();
106+
});
107+
108+
function onChangeScrollShrink(scrollShrink) {
109+
var closestContent = element.parent().find('md-content');
110+
111+
// If we have a content element, fake the call; this might still fail
112+
// if the content element isn't a sibling of the toolbar
113+
if (!contentElement && closestContent.length) {
114+
onMdContentLoad(null, closestContent);
115+
}
116+
117+
if (contentElement) {
118+
// Disable only if the attribute's expression evaluates to false
119+
if (scrollShrink === false) {
120+
disableScrollShrink();
121+
} else {
122+
enableScrollShrink();
123+
}
124+
}
125+
}
126+
127+
function enableScrollShrink() {
128+
contentElement.on('scroll', debouncedContentScroll);
129+
contentElement.attr('scroll-shrink', 'true');
130+
131+
$$rAF(updateToolbarHeight);
132+
133+
return function disableScrollShrink() {
134+
contentElement.off('scroll', debouncedContentScroll);
135+
contentElement.attr('scroll-shrink', 'false');
136+
137+
$$rAF(updateToolbarHeight);
138+
}
139+
}
140+
141+
92142
function onMdContentLoad($event, newContentEl) {
93143
// Toolbar and content must be siblings
94-
if (element.parent()[0] === newContentEl.parent()[0]) {
144+
if (newContentEl && element.parent()[0] === newContentEl.parent()[0]) {
95145
// unhook old content event listener if exists
96146
if (contentElement) {
97147
contentElement.off('scroll', debouncedContentScroll);
98148
}
99149

100-
newContentEl.on('scroll', debouncedContentScroll);
101-
newContentEl.attr('scroll-shrink', 'true');
102-
103150
contentElement = newContentEl;
104-
$$rAF(updateToolbarHeight);
151+
disableScrollShrink = enableScrollShrink();
105152
}
106153
}
107154

@@ -113,9 +160,12 @@ function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate )
113160
//
114161
// As the user scrolls down, the content will be transformed up slowly
115162
// to put the content underneath where the toolbar was.
116-
var margin = (-toolbarHeight * shrinkSpeedFactor) + 'px';
117-
contentElement.css('margin-top', margin);
118-
contentElement.css('margin-bottom', margin);
163+
var margin = (-toolbarHeight * shrinkSpeedFactor) + 'px';
164+
165+
contentElement.css({
166+
"margin-top": margin,
167+
"margin-bottom": margin
168+
});
119169

120170
onContentScroll();
121171
}
@@ -130,17 +180,17 @@ function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate )
130180
Math.max(0, y + scrollTop - prevScrollTop)
131181
);
132182

133-
element.css( $mdConstant.CSS.TRANSFORM, translateY([-y * shrinkSpeedFactor]) );
134-
contentElement.css( $mdConstant.CSS.TRANSFORM, translateY([(toolbarHeight - y) * shrinkSpeedFactor]) );
183+
element.css($mdConstant.CSS.TRANSFORM, translateY([-y * shrinkSpeedFactor]));
184+
contentElement.css($mdConstant.CSS.TRANSFORM, translateY([(toolbarHeight - y) * shrinkSpeedFactor]));
135185

136186
prevScrollTop = scrollTop;
137187

138-
$mdUtil.nextTick(function () {
188+
$mdUtil.nextTick(function() {
139189
var hasWhiteFrame = element.hasClass('md-whiteframe-z1');
140190

141-
if ( hasWhiteFrame && !y) {
191+
if (hasWhiteFrame && !y) {
142192
$animate.removeClass(element, 'md-whiteframe-z1');
143-
} else if ( !hasWhiteFrame && y ) {
193+
} else if (!hasWhiteFrame && y) {
144194
$animate.addClass(element, 'md-whiteframe-z1');
145195
}
146196
});

src/components/toolbar/toolbar.spec.js

Lines changed: 94 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
describe('<md-toolbar>', function() {
22

3+
var pageScope, element, controller;
4+
var $rootScope, $timeout;
5+
36
beforeEach(module('material.components.toolbar'));
7+
beforeEach(inject(function(_$rootScope_, _$timeout_) {
8+
$rootScope = _$rootScope_;
9+
$timeout = _$timeout_;
10+
}));
411

512
it('with scrollShrink, it should shrink scrollbar when going to bottom', inject(function($compile, $rootScope, $mdConstant, mdToolbarDirective, $$rAF) {
613

@@ -23,12 +30,20 @@ describe('<md-toolbar>', function() {
2330
toolbarCss[k] = v;
2431
});
2532
var contentCss = {};
26-
spyOn(contentEl, 'css').and.callFake(function(k, v) {
27-
contentCss[k] = v;
33+
spyOn(contentEl, 'css').and.callFake(function(properties, value) {
34+
if (angular.isObject(properties)) {
35+
for (k in properties) {
36+
if (properties.hasOwnProperty(k)) {
37+
contentCss[k] = properties[k];
38+
}
39+
}
40+
} else {
41+
contentCss[properties] = value;
42+
}
2843
});
2944

3045
// Manually link so we can give our own elements with spies on them
31-
mdToolbarDirective[0].link($rootScope, toolbar, {
46+
mdToolbarDirective[0].link($rootScope, toolbar, {
3247
mdScrollShrink: true,
3348
mdShrinkSpeedFactor: 1
3449
});
@@ -47,7 +62,7 @@ describe('<md-toolbar>', function() {
4762
// Fake scroll to the bottom
4863
contentEl.triggerHandler({
4964
type: 'scroll',
50-
target: { scrollTop: 500 }
65+
target: {scrollTop: 500}
5166
});
5267
$$rAF.flush();
5368

@@ -57,12 +72,86 @@ describe('<md-toolbar>', function() {
5772
// Fake scroll back to the top
5873
contentEl.triggerHandler({
5974
type: 'scroll',
60-
target: { scrollTop: 0 }
75+
target: {scrollTop: 0}
6176
});
6277
$$rAF.flush();
6378

6479
expect(toolbarCss[$mdConstant.CSS.TRANSFORM]).toEqual('translate3d(0,0px,0)');
6580
expect(contentCss[$mdConstant.CSS.TRANSFORM]).toEqual('translate3d(0,100px,0)');
6681

6782
}));
83+
84+
it('works without ng-if', inject(function() {
85+
build(
86+
'<div>' +
87+
' <md-toolbar md-scroll-shrink="true"></md-toolbar>' +
88+
' <md-content></md-content>' +
89+
'</div>'
90+
);
91+
92+
expect(element.find('md-content').attr('scroll-shrink')).toEqual('true');
93+
}));
94+
95+
it('works with ng-if', inject(function() {
96+
build(
97+
'<div>' +
98+
' <md-toolbar md-scroll-shrink="true" ng-if="shouldShowToolbar"></md-toolbar>' +
99+
' <md-content></md-content>' +
100+
'</div>'
101+
);
102+
103+
// It starts out undefined
104+
expect(element.find('md-content').attr('scroll-shrink')).toEqual(undefined);
105+
106+
// Change the ng-if to add the toolbar
107+
pageScope.$apply('shouldShowToolbar = true');
108+
expect(element.find('md-content').attr('scroll-shrink')).toEqual('true');
109+
110+
// Change the ng-if to remove the toolbar
111+
pageScope.$apply('shouldShowToolbar = false');
112+
expect(element.find('md-content').attr('scroll-shrink')).toEqual('false');
113+
}));
114+
115+
it('enables scroll shrink when the attribute has no value', function() {
116+
build(
117+
'<div>' +
118+
' <md-toolbar md-scroll-shrink></md-toolbar>' +
119+
' <md-content></md-content>' +
120+
'</div>'
121+
);
122+
123+
expect(element.find('md-content').attr('scroll-shrink')).toEqual('true');
124+
});
125+
126+
it('watches the value of scroll shrink', function() {
127+
build(
128+
'<div>' +
129+
' <md-toolbar md-scroll-shrink="shouldShrink"></md-toolbar>' +
130+
' <md-content></md-content>' +
131+
'</div>'
132+
);
133+
134+
// It starts out undefined which SHOULD add the scroll shrink because it acts as if no value
135+
// was specified
136+
expect(element.find('md-content').attr('scroll-shrink')).toEqual('true');
137+
138+
// Change the scrollShink to false
139+
pageScope.$apply('shouldShrink = false');
140+
expect(element.find('md-content').attr('scroll-shrink')).toEqual('false');
141+
142+
// Change the scrollShink to true
143+
pageScope.$apply('shouldShrink = true');
144+
expect(element.find('md-content').attr('scroll-shrink')).toEqual('true');
145+
});
146+
147+
function build(template) {
148+
inject(function($compile) {
149+
pageScope = $rootScope.$new();
150+
element = $compile(template)(pageScope);
151+
controller = element.controller('mdToolbar');
152+
153+
pageScope.$apply();
154+
$timeout.flush();
155+
});
156+
}
68157
});

0 commit comments

Comments
 (0)