diff --git a/src/components/autocomplete/autocomplete.spec.js b/src/components/autocomplete/autocomplete.spec.js index 4e70ebf1a04..0efe53002d0 100644 --- a/src/components/autocomplete/autocomplete.spec.js +++ b/src/components/autocomplete/autocomplete.spec.js @@ -321,6 +321,54 @@ describe('', function() { element.remove(); })); + it('should ensure the parent scope digests along with the current scope', inject(function($timeout, $material) { + var scope = createScope(null, {bang: 'boom'}); + var template = + '' + + ' ' + + ' {{bang}}' + + ' {{$index}}' + + ' {{item.display}}' + + ' ' + + ''; + var element = compile(template, scope); + var ctrl = element.controller('mdAutocomplete'); + var ul = element.find('ul'); + + $material.flushOutstandingAnimations(); + + // Focus the input + ctrl.focus(); + + element.scope().searchText = 'fo'; + + // Run our initial flush + $timeout.flush(); + waitForVirtualRepeat(element); + + // Wait for the next tick when the values will be updated + $timeout.flush(); + + var li = ul.find('li')[0]; + var parentScope = angular.element(li.querySelector('.find-parent-scope')).scope(); + + // When the autocomplete item's scope digests, ensure that the parent + // scope does too. + parentScope.bang = 'big'; + scope.$digest(); + + expect(li.querySelector('.find-parent-scope').innerHTML).toBe('big'); + + // Make sure we wrap up anything and remove the element + $timeout.flush(); + element.remove(); + })); + it('is hidden when no matches are found without an md-not-found template', inject(function($timeout, $material) { var scope = createScope(); var template = diff --git a/src/components/autocomplete/js/autocompleteParentScopeDirective.js b/src/components/autocomplete/js/autocompleteParentScopeDirective.js index c348e59cbc6..10618e83bcf 100644 --- a/src/components/autocomplete/js/autocompleteParentScopeDirective.js +++ b/src/components/autocomplete/js/autocompleteParentScopeDirective.js @@ -5,41 +5,71 @@ angular function MdAutocompleteItemScopeDirective($compile, $mdUtil) { return { restrict: 'AE', - link: postLink, - terminal: true + compile: compile, + terminal: true, + transclude: 'element' }; - function postLink(scope, element, attr) { - var ctrl = scope.$mdAutocompleteCtrl; - var newScope = ctrl.parent.$new(); - var itemName = ctrl.itemName; - - // Watch for changes to our scope's variables and copy them to the new scope - watchVariable('$index', '$index'); - watchVariable('item', itemName); - - // Recompile the contents with the new/modified scope - $compile(element.contents())(newScope); - - // Replace it if required - if (attr.hasOwnProperty('mdAutocompleteReplace')) { - element.after(element.contents()); - element.remove(); - } - - /** - * Creates a watcher for variables that are copied from the parent scope - * @param variable - * @param alias - */ - function watchVariable(variable, alias) { - newScope[alias] = scope[variable]; - - scope.$watch(variable, function(value) { - $mdUtil.nextTick(function() { - newScope[alias] = value; - }); + function compile(tElement, tAttr, transclude) { + return function postLink(scope, element, attr) { + var ctrl = scope.$mdAutocompleteCtrl; + var newScope = ctrl.parent.$new(); + var itemName = ctrl.itemName; + + // Watch for changes to our scope's variables and copy them to the new scope + watchVariable('$index', '$index'); + watchVariable('item', itemName); + + // Ensure that $digest calls on our scope trigger $digest on newScope. + connectScopes(); + + // Link the element against newScope. + transclude(newScope, function(clone) { + element.after(clone); }); - } + + /** + * Creates a watcher for variables that are copied from the parent scope + * @param variable + * @param alias + */ + function watchVariable(variable, alias) { + newScope[alias] = scope[variable]; + + scope.$watch(variable, function(value) { + $mdUtil.nextTick(function() { + newScope[alias] = value; + }); + }); + } + + /** + * Creates watchers on scope and newScope that ensure that for any + * $digest of scope, newScope is also $digested. + */ + function connectScopes() { + var scopeDigesting = false; + var newScopeDigesting = false; + + scope.$watch(function() { + if (newScopeDigesting || scopeDigesting) { + return; + } + + scopeDigesting = true; + scope.$$postDigest(function() { + if (!newScopeDigesting) { + newScope.$digest(); + } + + scopeDigesting = newScopeDigesting = false; + }); + }); + + newScope.$watch(function() { + newScopeDigesting = true; + }); + } + }; } } \ No newline at end of file diff --git a/src/components/autocomplete/js/highlightController.js b/src/components/autocomplete/js/highlightController.js index 21ca2fbf353..65c78d608ae 100644 --- a/src/components/autocomplete/js/highlightController.js +++ b/src/components/autocomplete/js/highlightController.js @@ -24,7 +24,7 @@ function MdHighlightCtrl ($scope, $element, $attrs) { $element.html(text.replace(regex, '$&')); }, true); - $element.on('$destroy', function () { watcher(); }); + $element.on('$destroy', watcher); } function sanitize (term) {