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

Commit 88ba1fd

Browse files
kseamonjelbourn
authored andcommitted
fix(autocomlete): Gets rid of uncompiled content flicker. Works around issues cased by the scope discontinuity in autocompleteParentScopeDirective.
add a unit test Closes #5637
1 parent 36efd3f commit 88ba1fd

File tree

3 files changed

+112
-34
lines changed

3 files changed

+112
-34
lines changed

src/components/autocomplete/autocomplete.spec.js

+48
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,54 @@ describe('<md-autocomplete>', function() {
321321
element.remove();
322322
}));
323323

324+
it('should ensure the parent scope digests along with the current scope', inject(function($timeout, $material) {
325+
var scope = createScope(null, {bang: 'boom'});
326+
var template =
327+
'<md-autocomplete' +
328+
' md-selected-item="selectedItem"' +
329+
' md-search-text="searchText"' +
330+
' md-items="item in match(searchText)"' +
331+
' md-item-text="item.display"' +
332+
' placeholder="placeholder">' +
333+
' <md-item-template>' +
334+
' <span class="find-parent-scope">{{bang}}</span>' +
335+
' <span class="find-index">{{$index}}</span>' +
336+
' <span class="find-item">{{item.display}}</span>' +
337+
' </md-item-template>' +
338+
'</md-autocomplete>';
339+
var element = compile(template, scope);
340+
var ctrl = element.controller('mdAutocomplete');
341+
var ul = element.find('ul');
342+
343+
$material.flushOutstandingAnimations();
344+
345+
// Focus the input
346+
ctrl.focus();
347+
348+
element.scope().searchText = 'fo';
349+
350+
// Run our initial flush
351+
$timeout.flush();
352+
waitForVirtualRepeat(element);
353+
354+
// Wait for the next tick when the values will be updated
355+
$timeout.flush();
356+
357+
var li = ul.find('li')[0];
358+
var parentScope = angular.element(li.querySelector('.find-parent-scope')).scope();
359+
360+
// When the autocomplete item's scope digests, ensure that the parent
361+
// scope does too.
362+
parentScope.bang = 'big';
363+
scope.$digest();
364+
365+
expect(li.querySelector('.find-parent-scope').innerHTML).toBe('big');
366+
367+
// Make sure we wrap up anything and remove the element
368+
$timeout.flush();
369+
element.remove();
370+
}));
371+
324372
it('is hidden when no matches are found without an md-not-found template', inject(function($timeout, $material) {
325373
var scope = createScope();
326374
var template =

src/components/autocomplete/js/autocompleteParentScopeDirective.js

+63-33
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,71 @@ angular
55
function MdAutocompleteItemScopeDirective($compile, $mdUtil) {
66
return {
77
restrict: 'AE',
8-
link: postLink,
9-
terminal: true
8+
compile: compile,
9+
terminal: true,
10+
transclude: 'element'
1011
};
1112

12-
function postLink(scope, element, attr) {
13-
var ctrl = scope.$mdAutocompleteCtrl;
14-
var newScope = ctrl.parent.$new();
15-
var itemName = ctrl.itemName;
16-
17-
// Watch for changes to our scope's variables and copy them to the new scope
18-
watchVariable('$index', '$index');
19-
watchVariable('item', itemName);
20-
21-
// Recompile the contents with the new/modified scope
22-
$compile(element.contents())(newScope);
23-
24-
// Replace it if required
25-
if (attr.hasOwnProperty('mdAutocompleteReplace')) {
26-
element.after(element.contents());
27-
element.remove();
28-
}
29-
30-
/**
31-
* Creates a watcher for variables that are copied from the parent scope
32-
* @param variable
33-
* @param alias
34-
*/
35-
function watchVariable(variable, alias) {
36-
newScope[alias] = scope[variable];
37-
38-
scope.$watch(variable, function(value) {
39-
$mdUtil.nextTick(function() {
40-
newScope[alias] = value;
41-
});
13+
function compile(tElement, tAttr, transclude) {
14+
return function postLink(scope, element, attr) {
15+
var ctrl = scope.$mdAutocompleteCtrl;
16+
var newScope = ctrl.parent.$new();
17+
var itemName = ctrl.itemName;
18+
19+
// Watch for changes to our scope's variables and copy them to the new scope
20+
watchVariable('$index', '$index');
21+
watchVariable('item', itemName);
22+
23+
// Ensure that $digest calls on our scope trigger $digest on newScope.
24+
connectScopes();
25+
26+
// Link the element against newScope.
27+
transclude(newScope, function(clone) {
28+
element.after(clone);
4229
});
43-
}
30+
31+
/**
32+
* Creates a watcher for variables that are copied from the parent scope
33+
* @param variable
34+
* @param alias
35+
*/
36+
function watchVariable(variable, alias) {
37+
newScope[alias] = scope[variable];
38+
39+
scope.$watch(variable, function(value) {
40+
$mdUtil.nextTick(function() {
41+
newScope[alias] = value;
42+
});
43+
});
44+
}
45+
46+
/**
47+
* Creates watchers on scope and newScope that ensure that for any
48+
* $digest of scope, newScope is also $digested.
49+
*/
50+
function connectScopes() {
51+
var scopeDigesting = false;
52+
var newScopeDigesting = false;
53+
54+
scope.$watch(function() {
55+
if (newScopeDigesting || scopeDigesting) {
56+
return;
57+
}
58+
59+
scopeDigesting = true;
60+
scope.$$postDigest(function() {
61+
if (!newScopeDigesting) {
62+
newScope.$digest();
63+
}
64+
65+
scopeDigesting = newScopeDigesting = false;
66+
});
67+
});
68+
69+
newScope.$watch(function() {
70+
newScopeDigesting = true;
71+
});
72+
}
73+
};
4474
}
4575
}

src/components/autocomplete/js/highlightController.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function MdHighlightCtrl ($scope, $element, $attrs) {
2424

2525
$element.html(text.replace(regex, '<span class="highlight">$&</span>'));
2626
}, true);
27-
$element.on('$destroy', function () { watcher(); });
27+
$element.on('$destroy', watcher);
2828
}
2929

3030
function sanitize (term) {

0 commit comments

Comments
 (0)