Skip to content

Commit 9dcdf73

Browse files
topherfangiokennethcachia
authored andcommitted
fix(mdToolbar): Better fix for ng-if that allows ng-controller.
After modifying the `<md-toolbar>` directive to allow both `md-scroll-shrink` and `ng-if`, we accidentally broke support for also using `ng-controller` as well as some layout problems due to the transclusion and a wrapping `<div>`. This reverts those changes but still allows for `ng-if` to work. Fixes angular#4144. Closes angular#4423.
1 parent b2a83c7 commit 9dcdf73

File tree

4 files changed

+93
-82
lines changed

4 files changed

+93
-82
lines changed

src/components/toolbar/demoScrollShrink/index.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<div ng-controller="AppCtrl" layout="column" style="height:600px">
22

3-
<md-toolbar md-scroll-shrink>
3+
<md-toolbar md-scroll-shrink ng-if="true" ng-controller="TitleController">
44
<div class="md-toolbar-tools">
55
<h3>
6-
<span>My App Title</span>
6+
<span>{{title}}</span>
77
</h3>
88
</div>
99
</md-toolbar>

src/components/toolbar/demoScrollShrink/script.js

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
var app = angular.module('toolbarDemo2', ['ngMaterial']);
22

3+
app.controller('TitleController', function($scope) {
4+
$scope.title = 'My App Title';
5+
});
6+
37
app.controller('AppCtrl', function($scope) {
48
var imagePath = 'img/list/60.jpeg';
59

src/components/toolbar/toolbar.js

+69-60
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ angular.module('material.components.toolbar', [
4747
*
4848
* @param {boolean=} md-scroll-shrink Whether the header should shrink away as
4949
* the user scrolls down, and reveal itself as the user scrolls up.
50-
* Note: for scrollShrink to work, the toolbar must be a sibling of a
51-
* `md-content` element, placed before it. See the scroll shrink demo.
50+
* _**Note (1):** for scrollShrink to work, the toolbar must be a sibling of a
51+
* `md-content` element, placed before it. See the scroll shrink demo._
52+
* _**Note (2):** The `md-scroll-shrink` attribute is only parsed on component
53+
* initialization, it does not watch for scope changes._
5254
*
5355
*
5456
* @param {number=} md-shrink-speed-factor How much to change the speed of the toolbar's
@@ -61,84 +63,66 @@ function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate) {
6163

6264
return {
6365
restrict: 'E',
64-
template: '<div ng-transclude></div>',
65-
transclude: true,
66-
controller: angular.noop,
67-
scope: {
68-
scrollShrink: '=?mdScrollShrink'
69-
},
66+
7067
link: function(scope, element, attr) {
7168

7269
$mdTheming(element);
7370

7471
setupScrollShrink();
7572

7673
function setupScrollShrink() {
74+
75+
var toolbarHeight;
76+
var contentElement;
77+
var disableScrollShrink = angular.noop;
78+
7779
// Current "y" position of scroll
78-
var y = 0;
7980
// Store the last scroll top position
81+
var y = 0;
8082
var prevScrollTop = 0;
81-
8283
var shrinkSpeedFactor = attr.mdShrinkSpeedFactor || 0.5;
8384

84-
var toolbarHeight;
85-
var contentElement;
86-
8785
var debouncedContentScroll = $$rAF.throttle(onContentScroll);
8886
var debouncedUpdateHeight = $mdUtil.debounce(updateToolbarHeight, 5 * 1000);
8987

90-
var disableScrollShrink;
91-
9288
// Wait for $mdContentLoaded event from mdContent directive.
9389
// If the mdContent element is a sibling of our toolbar, hook it up
9490
// to scroll events.
91+
9592
scope.$on('$mdContentLoaded', onMdContentLoad);
9693

9794
// If the toolbar is used inside an ng-if statement, we may miss the
9895
// $mdContentLoaded event, so we attempt to fake it if we have a
9996
// md-content close enough.
100-
scope.$watch('scrollShrink', onChangeScrollShrink);
97+
98+
attr.$observe('mdScrollShrink', onChangeScrollShrink);
10199

102100
// If the scope is destroyed (which could happen with ng-if), make sure
103101
// to disable scroll shrinking again
104-
scope.$on('$destroy', function() {
105-
disableScrollShrink();
106-
});
107102

108-
function onChangeScrollShrink(scrollShrink) {
103+
scope.$on('$destroy', disableScrollShrink );
104+
105+
/**
106+
*
107+
*/
108+
function onChangeScrollShrink(shrinkWithScroll) {
109109
var closestContent = element.parent().find('md-content');
110110

111111
// If we have a content element, fake the call; this might still fail
112112
// if the content element isn't a sibling of the toolbar
113+
113114
if (!contentElement && closestContent.length) {
114115
onMdContentLoad(null, closestContent);
115116
}
116117

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);
118+
// Disable only if the attribute's expression evaluates to false
132119

133-
return function disableScrollShrink() {
134-
contentElement.off('scroll', debouncedContentScroll);
135-
contentElement.attr('scroll-shrink', 'false');
136-
137-
$$rAF(updateToolbarHeight);
138-
}
120+
if ( shrinkWithScroll ) disableScrollShrink = enableScrollShrink();
139121
}
140122

141-
123+
/**
124+
*
125+
*/
142126
function onMdContentLoad($event, newContentEl) {
143127
// Toolbar and content must be siblings
144128
if (newContentEl && element.parent()[0] === newContentEl.parent()[0]) {
@@ -152,24 +136,9 @@ function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate) {
152136
}
153137
}
154138

155-
function updateToolbarHeight() {
156-
toolbarHeight = element.prop('offsetHeight');
157-
// Add a negative margin-top the size of the toolbar to the content el.
158-
// The content will start transformed down the toolbarHeight amount,
159-
// so everything looks normal.
160-
//
161-
// As the user scrolls down, the content will be transformed up slowly
162-
// to put the content underneath where the toolbar was.
163-
var margin = (-toolbarHeight * shrinkSpeedFactor) + 'px';
164-
165-
contentElement.css({
166-
"margin-top": margin,
167-
"margin-bottom": margin
168-
});
169-
170-
onContentScroll();
171-
}
172-
139+
/**
140+
*
141+
*/
173142
function onContentScroll(e) {
174143
var scrollTop = e ? e.target.scrollTop : prevScrollTop;
175144

@@ -197,6 +166,46 @@ function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate) {
197166

198167
}
199168

169+
/**
170+
*
171+
*/
172+
function enableScrollShrink() {
173+
if ( !contentElement ) return angular.noop; // no md-content
174+
175+
contentElement.on('scroll', debouncedContentScroll);
176+
contentElement.attr('scroll-shrink', 'true');
177+
178+
$$rAF(updateToolbarHeight);
179+
180+
return function disableScrollShrink() {
181+
contentElement.off('scroll', debouncedContentScroll);
182+
contentElement.attr('scroll-shrink', 'false');
183+
184+
$$rAF(updateToolbarHeight);
185+
}
186+
}
187+
188+
/**
189+
*
190+
*/
191+
function updateToolbarHeight() {
192+
toolbarHeight = element.prop('offsetHeight');
193+
// Add a negative margin-top the size of the toolbar to the content el.
194+
// The content will start transformed down the toolbarHeight amount,
195+
// so everything looks normal.
196+
//
197+
// As the user scrolls down, the content will be transformed up slowly
198+
// to put the content underneath where the toolbar was.
199+
var margin = (-toolbarHeight * shrinkSpeedFactor) + 'px';
200+
201+
contentElement.css({
202+
"margin-top": margin,
203+
"margin-bottom": margin
204+
});
205+
206+
onContentScroll();
207+
}
208+
200209
}
201210

202211
}

src/components/toolbar/toolbar.spec.js

+18-20
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ describe('<md-toolbar>', function() {
33
var pageScope, element, controller;
44
var $rootScope, $timeout;
55

6-
beforeEach(module('material.components.toolbar'));
6+
beforeEach(function() {
7+
module('material.components.toolbar', function($controllerProvider) {
8+
$controllerProvider.register('MockController', function() {});
9+
});
10+
});
711
beforeEach(inject(function(_$rootScope_, _$timeout_) {
812
$rootScope = _$rootScope_;
913
$timeout = _$timeout_;
@@ -45,7 +49,8 @@ describe('<md-toolbar>', function() {
4549
// Manually link so we can give our own elements with spies on them
4650
mdToolbarDirective[0].link($rootScope, toolbar, {
4751
mdScrollShrink: true,
48-
mdShrinkSpeedFactor: 1
52+
mdShrinkSpeedFactor: 1,
53+
$observe: function() {}
4954
});
5055

5156
$rootScope.$apply();
@@ -103,44 +108,37 @@ describe('<md-toolbar>', function() {
103108
// It starts out undefined
104109
expect(element.find('md-content').attr('scroll-shrink')).toEqual(undefined);
105110

106-
// Change the ng-if to add the toolbar
111+
// Change the ng-if to add the toolbar which then injects a scrollShrink
112+
// on the mdContent
107113
pageScope.$apply('shouldShowToolbar = true');
108114
expect(element.find('md-content').attr('scroll-shrink')).toEqual('true');
109115

110116
// Change the ng-if to remove the toolbar
111117
pageScope.$apply('shouldShowToolbar = false');
112-
expect(element.find('md-content').attr('scroll-shrink')).toEqual('false');
118+
expect(element.find('md-toolbar').length).toBe(0);
113119
}));
114120

115-
it('enables scroll shrink when the attribute has no value', function() {
121+
// The toolbar is like a container component, so we want to make sure it works with ng-controller
122+
it('works with ng-controller', inject(function($exceptionHandler) {
116123
build(
117124
'<div>' +
118-
' <md-toolbar md-scroll-shrink></md-toolbar>' +
125+
' <md-toolbar md-scroll-shrink ng-controller="MockController"></md-toolbar>' +
119126
' <md-content></md-content>' +
120127
'</div>'
121128
);
122129

123-
expect(element.find('md-content').attr('scroll-shrink')).toEqual('true');
124-
});
130+
// Expect no errors
131+
expect($exceptionHandler.errors).toEqual([]);
132+
}));
125133

126-
it('watches the value of scroll shrink', function() {
134+
it('enables scroll shrink when the attribute has no value', function() {
127135
build(
128136
'<div>' +
129-
' <md-toolbar md-scroll-shrink="shouldShrink"></md-toolbar>' +
137+
' <md-toolbar md-scroll-shrink></md-toolbar>' +
130138
' <md-content></md-content>' +
131139
'</div>'
132140
);
133141

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');
144142
expect(element.find('md-content').attr('scroll-shrink')).toEqual('true');
145143
});
146144

0 commit comments

Comments
 (0)