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

Commit

Permalink
fix(mdChips): Fix error with audocomplete inside readonly chips and a…
Browse files Browse the repository at this point in the history
…llow usage inside ng-repeat.

When an autocomplete was used inside of mdChips with the readonly
attribute set to true, it would throw an erroneous error that
would cause the component to not be properly configured and thus
stop working.

Also, using <md-chips> inside of an ng-repeat would fail. Thanks to
@robertmesserle for the guidance in getting this resolved.

fixes #2841, fixes #3482
  • Loading branch information
topherfangio committed Aug 13, 2015
1 parent aa7f83a commit d2da1c7
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 21 deletions.
70 changes: 58 additions & 12 deletions src/components/chips/chips.spec.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
describe('<md-chips>', function() {
var scope;

var scope, $exceptionHandler, $timeout;
var BASIC_CHIP_TEMPLATE =
'<md-chips ng-model="items"></md-chips>';
var CHIP_APPEND_TEMPLATE =
'<md-chips ng-model="items" md-on-append="appendChip($chip)"></md-chips>';
var CHIP_REMOVE_TEMPLATE =
'<md-chips ng-model="items" md-on-remove="removeChip($chip, $index)"></md-chips>';
var CHIP_READONLY_AUTOCOMPLETE_TEMPLATE =
'<md-chips ng-model="items" readonly="true">' +
' <md-autocomplete md-items="item in [\'hi\', \'ho\', \'he\']"></md-autocomplete>' +
'</md-chips>';

describe('with no overrides', function() {

beforeEach(module('material.components.chips', 'material.components.autocomplete'));
beforeEach(inject(function($rootScope) {
beforeEach(inject(function($rootScope, _$exceptionHandler_, _$timeout_) {
scope = $rootScope.$new();
scope.items = ['Apple', 'Banana', 'Orange'];
$exceptionHandler = _$exceptionHandler_;
$timeout = _$timeout_;
}));

describe('basic functionality', function() {
Expand Down Expand Up @@ -155,6 +159,14 @@ describe('<md-chips>', function() {
expect(scope.items[3].uppername).toBe('GRAPE');
});

it('should not throw an error when using readonly with an autocomplete', function() {
var element = buildChips(CHIP_READONLY_AUTOCOMPLETE_TEMPLATE);

$timeout.flush();

expect($exceptionHandler.errors).toEqual([]);
});

it('should disallow duplicate object chips', function() {
var element = buildChips(CHIP_APPEND_TEMPLATE);
var ctrl = element.controller('mdChips');
Expand Down Expand Up @@ -270,6 +282,17 @@ describe('<md-chips>', function() {
<md-chip>Baseball</md-chip>\
<md-chip>{{chipItem}}</md-chip>\
</md-chips>';

var STATIC_CHIPS_NGREPEAT_TEMPLATE = '\
<div>\
<div ng-repeat="i in [1,2,3]">\
<md-chips>\
<md-chip>{{i}}</md-chip>\
</md-chips>\
</div>\
</div>\
';

it('should transclude static chips', inject(function($timeout) {
scope.chipItem = 'Football';
var element = buildChips(STATIC_CHIPS_TEMPLATE);
Expand All @@ -283,6 +306,25 @@ describe('<md-chips>', function() {
expect(chips[2].innerHTML).toContain('Baseball');
expect(chips[3].innerHTML).toContain('Football');
}));

it('allows ng-repeat outside of md-chips', function() {
var element = buildChips(STATIC_CHIPS_NGREPEAT_TEMPLATE);
var ctrl = element.controller('mdChips');

$timeout.flush();

var chipsArray = getChipsElements(element);
var chipArray = getChipElements(element);

// Check the lengths
expect(chipsArray.length).toBe(3);
expect(chipArray.length).toBe(3);

// Check the chip's text
expect(chipArray[0].innerHTML).toContain('1');
expect(chipArray[1].innerHTML).toContain('2');
expect(chipArray[2].innerHTML).toContain('3');
});
});

describe('<md-chip-remove>', function() {
Expand Down Expand Up @@ -331,11 +373,11 @@ describe('<md-chips>', function() {
' <md-chip-template><div class="mychiptemplate">[[$chip]]</div></md-chip-template>' +
'</md-chips>';
var element = buildChips(template);
var chips = element.find('md-chip');
var chips = element[0].querySelectorAll('md-chip .mychiptemplate');

expect(chips.eq(0).text().trim()).toEqual('Apple');
expect(chips.eq(1).text().trim()).toEqual('Banana');
expect(chips.eq(2).text().trim()).toEqual('Orange');
expect(angular.element(chips[0]).text().trim()).toEqual('Apple');
expect(angular.element(chips[1]).text().trim()).toEqual('Banana');
expect(angular.element(chips[2]).text().trim()).toEqual('Orange');
});

it('should not interpolate old-style tags in a user-provided chip template', function() {
Expand All @@ -344,11 +386,11 @@ describe('<md-chips>', function() {
' <md-chip-template><div class="mychiptemplate">{{$chip}}</div></md-chip-template>' +
'</md-chips>';
var element = buildChips(template);
var chips = element.find('md-chip');
var chips = element[0].querySelectorAll('md-chip .mychiptemplate');

expect(chips.eq(0).text().trim()).toEqual('{{$chip}}');
expect(chips.eq(1).text().trim()).toEqual('{{$chip}}');
expect(chips.eq(2).text().trim()).toEqual('{{$chip}}');
expect(angular.element(chips[0]).text().trim()).toEqual('{{$chip}}');
expect(angular.element(chips[1]).text().trim()).toEqual('{{$chip}}');
expect(angular.element(chips[2]).text().trim()).toEqual('{{$chip}}');
});
});

Expand Down Expand Up @@ -385,6 +427,10 @@ describe('<md-chips>', function() {
ctrl.inputKeydown(event);
}

function getChipsElements(root) {
return angular.element(root[0].querySelectorAll('md-chips'));
}

function getChipElements(root) {
return angular.element(root[0].querySelectorAll('md-chip'));
}
Expand Down
26 changes: 17 additions & 9 deletions src/components/chips/js/chipsDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
* MDChips Directive Definition
*/
function MdChips ($mdTheming, $mdUtil, $compile, $log, $timeout) {
// Run our templates through $mdUtil.processTemplate() to allow custom start/end symbosl
// Run our templates through $mdUtil.processTemplate() to allow custom start/end symbols
convertTemplates();

return {
Expand All @@ -149,7 +149,7 @@
// name with '$', Angular won't write it into the DOM. The cloned
// element propagates to the link function via the attrs argument,
// where various contained-elements can be consumed.
var content = attrs['$mdUserTemplate'] = element.clone();
attrs['$mdUserTemplate'] = element.clone();
return MD_CHIPS_TEMPLATE;
},
require: ['mdChips'],
Expand Down Expand Up @@ -255,19 +255,27 @@
// is complete (due to their nested nature). Wait a tick before looking for them to
// configure the controller.
if (chipInputTemplate != CHIP_INPUT_TEMPLATE) {
$timeout(function() {
if (chipInputTemplate.indexOf('<md-autocomplete') === 0)
mdChipsCtrl
.configureAutocomplete(element.find('md-autocomplete')
.controller('mdAutocomplete'));
mdChipsCtrl.configureUserInput(element.find('input'));
// The autocomplete will not appear until the readonly attribute is not true (i.e.
// false or undefined), so we have to watch the readonly and then on the next tick
// after the chip transclusion has run, we can configure the autocomplete and user
// input.
scope.$watch('$mdChipsCtrl.readonly', function(readonly) {
if (!readonly) {
$mdUtil.nextTick(function(){
if (chipInputTemplate.indexOf('<md-autocomplete') === 0)
mdChipsCtrl
.configureAutocomplete(element.find('md-autocomplete')
.controller('mdAutocomplete'));
mdChipsCtrl.configureUserInput(element.find('input'));
});
}
});
}
}

// Compile with the parent's scope and prepend any static chips to the wrapper.
if (staticChips.length > 0) {
var compiledStaticChips = $compile(staticChips)(scope.$parent);
var compiledStaticChips = $compile(staticChips.clone())(scope.$parent);
$timeout(function() { element.find('md-chips-wrap').prepend(compiledStaticChips); });
}
};
Expand Down

0 comments on commit d2da1c7

Please sign in to comment.