Skip to content

Commit

Permalink
fix(mdAutocomplete): Compile autocomplete template against proper scope.
Browse files Browse the repository at this point in the history
After the performance modifications to make autocomplete use the
virtual repeat directive, the autocomplete no longer compiled the
contents of the dropdown against the proper parent scope

This PR fixes it by manually compiling against the proper parent scope
and copying over the necessary `item` and `$index` properties.

Fixes angular#4390. Fixes angular#4495. Closes angular#4391.
  • Loading branch information
topherfangio authored and kennethcachia committed Sep 23, 2015
1 parent 1a58323 commit 189dbfe
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 24 deletions.
42 changes: 42 additions & 0 deletions src/components/autocomplete/autocomplete.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,48 @@ describe('<md-autocomplete>', function () {

element.remove();
}));

it('should compile the template against the parent scope', inject(function ($timeout) {
var scope = createScope(null, { bang: 'boom' });
var template = '\
<md-autocomplete\
md-selected-item="selectedItem"\
md-search-text="searchText"\
md-items="item in match(searchText)"\
md-item-text="item.display"\
placeholder="placeholder">\
<md-item-template>\
<span class="find-parent-scope">{{bang}}</span>\
<span class="find-index">{{$index}}</span>\
<span class="find-item">{{item.display}}</span>\
</md-item-template>\
</md-autocomplete>';
var element = compile(template, scope);
var ctrl = element.controller('mdAutocomplete');
var ul = element.find('ul');

expect(scope.bang).toBe('boom');

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];

// Expect it to be compiled against the parent scope and have our variables copied
expect(li.querySelector('.find-parent-scope').innerHTML).toBe('boom');
expect(li.querySelector('.find-index').innerHTML).toBe('0');
expect(li.querySelector('.find-item').innerHTML).toBe('foo');

$timeout.flush();

element.remove();
}));
});

describe('xss prevention', function () {
Expand Down
2 changes: 1 addition & 1 deletion src/components/autocomplete/js/autocompleteDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ function MdAutocomplete () {
var templateTag = element.find('md-item-template').detach(),
html = templateTag.length ? templateTag.html() : element.html();
if (!templateTag.length) element.empty();
return html;
return '<md-autocomplete-parent-scope md-autocomplete-replace>' + html + '</md-autocomplete-parent-scope>';
}

function getNoItemsTemplate() {
Expand Down
34 changes: 34 additions & 0 deletions src/components/autocomplete/js/autocompleteParentScopeDirective.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
angular
.module('material.components.autocomplete')
.directive('mdAutocompleteParentScope', MdAutocompleteItemScopeDirective);

function MdAutocompleteItemScopeDirective($compile, $mdUtil) {
return {
restrict: 'AE',
link: postLink,
terminal: true
};

function postLink(scope, element, attr) {
var newScope = scope.$mdAutocompleteCtrl.parent.$new();
var relevantVariables = ['item', '$index'];

// Watch for changes to our scope's variables and copy them to the new scope
angular.forEach(relevantVariables, function(variable){
scope.$watch(variable, function(value) {
$mdUtil.nextTick(function() {
newScope[variable] = value;
});
});
});

// 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();
}
}
}
23 changes: 0 additions & 23 deletions src/components/autocomplete/js/parentScope.js

This file was deleted.

0 comments on commit 189dbfe

Please sign in to comment.