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

Commit 44140ce

Browse files
devversionThomasBurleson
authored andcommitted
fix(subheader): fix hidden directives inside of sticky clone.
* Currently the subheader was not working properly with `ng-if`'s inside of its content. This was caused by a wrong recompile of the already compiled transcluded clone. * This commit also fixes the meaningless / useless tests. Before we never imported the `subheader` module into its tests. Also we never tested the $mdSticky service together with the subheader, because we had a really ugly mock for the `$mdSticky` service, which just tested the call arguments. Fixes #8500. Closes #8504. Closes #8505
1 parent 715dd34 commit 44140ce

File tree

2 files changed

+95
-43
lines changed

2 files changed

+95
-43
lines changed

src/components/subheader/subheader.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,17 @@ function MdSubheaderDirective($mdSticky, $compile, $mdTheming, $mdUtil) {
7777
// compiled clone below will only be a comment tag (since they replace their elements with
7878
// a comment) which cannot be properly passed to the $mdSticky; so we wrap it in our own
7979
// DIV to ensure we have something $mdSticky can use
80-
var wrapperHtml = '<div class="_md-subheader-wrapper">' + outerHTML + '</div>';
81-
var stickyClone = $compile(wrapperHtml)(scope);
80+
var wrapper = angular.element('<div class="_md-subheader-wrapper">' + outerHTML + '</div>');
8281

83-
// Append the sticky
84-
$mdSticky(scope, element, stickyClone);
82+
// Immediately append our transcluded clone into the wrapper.
83+
// We don't have to recompile the element again, because the clone is already
84+
// compiled in it's transclusion scope. If we recompile the outerHTML of the new clone, we would lose
85+
// our ngIf's and other previous registered bindings / properties.
86+
getContent(wrapper).append(clone);
8587

86-
// Delay initialization until after any `ng-if`/`ng-repeat`/etc has finished before
87-
// attempting to create the clone
88-
$mdUtil.nextTick(function() {
89-
getContent(stickyClone).append(clone);
90-
});
88+
// Make the element sticky and provide the stickyClone our self, to avoid recompilation of the subheader
89+
// element.
90+
$mdSticky(scope, element, wrapper);
9191
});
9292
}
9393
}
Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,31 @@
11
describe('mdSubheader', function() {
2-
var $mdStickyMock,
3-
basicHtml = '<md-subheader>Hello world!</md-header>',
4-
pageScope, element, controller;
5-
2+
var BASIC_SUBHEADER = '<md-subheader>Hello world!</md-subheader>';
3+
var pageScope, element, controller, contentElement;
64
var $rootScope, $timeout, $exceptionHandler;
75

8-
beforeEach(module('material.components.subheader', function($provide) {
9-
$mdStickyMock = function() {
10-
$mdStickyMock.args = Array.prototype.slice.call(arguments);
11-
};
12-
$provide.value('$mdSticky', $mdStickyMock);
13-
}));
6+
beforeEach(module('material.components.subheader'));
147

15-
beforeEach(inject(function(_$rootScope_, _$timeout_, _$exceptionHandler_) {
16-
$rootScope = _$rootScope_;
17-
$timeout = _$timeout_;
18-
$exceptionHandler = _$exceptionHandler_;
8+
beforeEach(inject(function($injector) {
9+
$rootScope = $injector.get('$rootScope');
10+
$timeout = $injector.get('$timeout');
11+
$exceptionHandler = $injector.get('$exceptionHandler');
1912
}));
2013

2114

2215
it('should have `._md` class indicator', inject(function() {
23-
build('<div><md-subheader>Hello {{ to }}!</md-subheader></div>');
24-
pageScope.to = 'world';
25-
pageScope.$digest();
16+
build(BASIC_SUBHEADER);
2617

27-
expect(element.children().hasClass('_md')).toBe(true);
18+
expect(element.hasClass('_md')).toBe(true);
2819
}));
2920

3021

3122
it('preserves content', function() {
32-
build('<div><md-subheader>Hello {{ to }}!</md-subheader></div>');
23+
build(
24+
'<div>' +
25+
'<md-subheader>Hello {{ to }}!</md-subheader>' +
26+
'</div>'
27+
);
28+
3329
pageScope.to = 'world';
3430
pageScope.$digest();
3531

@@ -39,23 +35,22 @@ describe('mdSubheader', function() {
3935
});
4036

4137
it('implements $mdSticky', function() {
42-
build(basicHtml);
38+
build(BASIC_SUBHEADER);
4339

44-
expect($mdStickyMock.args[0]).toBe(pageScope);
40+
var cloneScope = element.scope();
41+
42+
expect(cloneScope).toBe(pageScope);
4543
});
4644

4745
it('applies the theme to the header and clone', function() {
48-
build('<div md-theme="somethingElse">' + basicHtml + '</div>');
49-
50-
// Grab the real element
51-
var element = $mdStickyMock.args[1];
46+
build('<div md-theme="somethingElse">' + BASIC_SUBHEADER + '</div>');
5247

5348
// The subheader now wraps the clone in a DIV in case of ng-if usage, so we have to search for
5449
// the proper element.
55-
var clone = angular.element($mdStickyMock.args[2][0].querySelector('.md-subheader'));
50+
var clone = getCloneElement();
5651

57-
expect(element.hasClass('md-somethingElse-theme')).toBe(true);
58-
expect(clone.hasClass('md-somethingElse-theme')).toBe(true);
52+
expect(getSubheader().classList).toContain('md-somethingElse-theme');
53+
expect(getSubheader(clone).classList).toContain('md-somethingElse-theme');
5954
});
6055

6156
it('applies the proper scope to the clone', function() {
@@ -64,11 +59,10 @@ describe('mdSubheader', function() {
6459
pageScope.to = 'world';
6560
pageScope.$apply();
6661

67-
var element = $mdStickyMock.args[1];
68-
var clone = $mdStickyMock.args[2];
62+
var clone = getCloneElement();
6963

70-
expect(element.text().trim()).toEqual('Hello world!');
71-
expect(clone.text().trim()).toEqual('Hello world!');
64+
expect(getSubheader().textContent.trim()).toEqual('Hello world!');
65+
expect(getSubheader(clone).textContent.trim()).toEqual('Hello world!');
7266
});
7367

7468
it('supports ng-if', function() {
@@ -78,6 +72,39 @@ describe('mdSubheader', function() {
7872
expect(element[0].querySelectorAll('.md-subheader').length).toEqual(1);
7973
});
8074

75+
it('should support ng-if inside of stickyClone', function() {
76+
build(
77+
'<div>' +
78+
'<md-subheader>' +
79+
'Foo' +
80+
'<span ng-if="isBar">Bar</span>' +
81+
'</md-subheader>' +
82+
'</div>'
83+
);
84+
85+
var clone = getCloneElement()[0];
86+
87+
expect(clone.textContent.trim()).toBe('Foo');
88+
89+
pageScope.$apply('isBar = true');
90+
91+
expect(clone.textContent.trim()).toBe('FooBar');
92+
});
93+
94+
it('should work with a ng-if directive inside of the stickyClone', function() {
95+
build(
96+
'<div>' +
97+
'<md-subheader>' +
98+
'<span ng-repeat="item in [0, 1, 2, 3]">{{ item }}</span>' +
99+
'</md-subheader>' +
100+
'</div>'
101+
);
102+
103+
var cloneContent = getCloneElement()[0].querySelector('._md-subheader-content');
104+
105+
expect(cloneContent.children.length).toBe(4);
106+
});
107+
81108
it('supports ng-repeat', function() {
82109
build('<div><md-subheader ng-repeat="i in [1,2,3]">Test {{i}}</md-subheader></div>');
83110

@@ -86,13 +113,38 @@ describe('mdSubheader', function() {
86113
});
87114

88115
function build(template) {
89-
inject(function($compile) {
116+
inject(function($compile, $timeout) {
90117
pageScope = $rootScope.$new();
91-
element = $compile(template)(pageScope);
118+
119+
contentElement = $compile('<md-content>' + template + '</md-content>')(pageScope);
120+
121+
// Flush the timeout, which prepends the sticky clone to the md-content.
122+
$timeout.flush();
123+
124+
// When the contentElement only has only one children then the current
125+
// browser supports sticky elements natively.
126+
if (contentElement.children().length === 1) {
127+
element = getCloneElement();
128+
} else {
129+
// When the browser doesn't support sticky elements natively we will have a sticky clone.
130+
// The sticky clone element will be always prepended, which means that we have to use the child
131+
// at the second position.
132+
element = contentElement.children().eq(1);
133+
}
134+
92135
controller = element.controller('mdSubheader');
93136

94137
pageScope.$apply();
95138
$timeout.flush();
96139
});
97140
}
141+
142+
function getSubheader(el) {
143+
return (el || element)[0].querySelector('.md-subheader');
144+
}
145+
146+
function getCloneElement() {
147+
// The clone element will be always prepended, which means that we have to get the child at index zero.
148+
return contentElement.children().eq(0);
149+
}
98150
});

0 commit comments

Comments
 (0)