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

Commit 2ad43e3

Browse files
committed
fix(mdChips): Fix error with audocomplete inside readonly chips and allow 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
1 parent aa7f83a commit 2ad43e3

File tree

2 files changed

+71
-17
lines changed

2 files changed

+71
-17
lines changed

src/components/chips/chips.spec.js

+54-8
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
describe('<md-chips>', function() {
2-
var scope;
3-
2+
var scope, $exceptionHandler, $timeout;
43
var BASIC_CHIP_TEMPLATE =
54
'<md-chips ng-model="items"></md-chips>';
65
var CHIP_APPEND_TEMPLATE =
76
'<md-chips ng-model="items" md-on-append="appendChip($chip)"></md-chips>';
87
var CHIP_REMOVE_TEMPLATE =
98
'<md-chips ng-model="items" md-on-remove="removeChip($chip, $index)"></md-chips>';
9+
var CHIP_READONLY_AUTOCOMPLETE_TEMPLATE =
10+
'<md-chips ng-model="items" readonly="true">' +
11+
' <md-autocomplete md-items="item in [\'hi\', \'ho\', \'he\']"></md-autocomplete>' +
12+
'</md-chips>';
1013

1114
describe('with no overrides', function() {
12-
1315
beforeEach(module('material.components.chips', 'material.components.autocomplete'));
14-
beforeEach(inject(function($rootScope) {
16+
beforeEach(inject(function($rootScope, _$exceptionHandler_, _$timeout_) {
1517
scope = $rootScope.$new();
1618
scope.items = ['Apple', 'Banana', 'Orange'];
19+
$exceptionHandler = _$exceptionHandler_;
20+
$timeout = _$timeout_;
1721
}));
1822

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

162+
it('should not throw an error when using readonly with an autocomplete', function() {
163+
var element = buildChips(CHIP_READONLY_AUTOCOMPLETE_TEMPLATE);
164+
165+
$timeout.flush();
166+
167+
expect($exceptionHandler.errors).toEqual([]);
168+
});
169+
158170
it('should disallow duplicate object chips', function() {
159171
var element = buildChips(CHIP_APPEND_TEMPLATE);
160172
var ctrl = element.controller('mdChips');
@@ -270,6 +282,17 @@ describe('<md-chips>', function() {
270282
<md-chip>Baseball</md-chip>\
271283
<md-chip>{{chipItem}}</md-chip>\
272284
</md-chips>';
285+
286+
var STATIC_CHIPS_NGREPEAT_TEMPLATE = '\
287+
<div>\
288+
<div ng-repeat="i in [1,2,3]">\
289+
<md-chips>\
290+
<md-chip>{{i}}</md-chip>\
291+
</md-chips>\
292+
</div>\
293+
</div>\
294+
';
295+
273296
it('should transclude static chips', inject(function($timeout) {
274297
scope.chipItem = 'Football';
275298
var element = buildChips(STATIC_CHIPS_TEMPLATE);
@@ -283,6 +306,25 @@ describe('<md-chips>', function() {
283306
expect(chips[2].innerHTML).toContain('Baseball');
284307
expect(chips[3].innerHTML).toContain('Football');
285308
}));
309+
310+
it('allows ng-repeat outside of md-chips', function() {
311+
var element = buildChips(STATIC_CHIPS_NGREPEAT_TEMPLATE);
312+
var ctrl = element.controller('mdChips');
313+
314+
$timeout.flush();
315+
316+
var chipsArray = getChipsElements(element);
317+
var chipArray = getChipElements(element);
318+
319+
// Check the lengths
320+
expect(chipsArray.length).toBe(3);
321+
expect(chipArray.length).toBe(3);
322+
323+
// Check the chip's text
324+
expect(chipArray[0].innerHTML).toContain('1');
325+
expect(chipArray[1].innerHTML).toContain('2');
326+
expect(chipArray[2].innerHTML).toContain('3');
327+
});
286328
});
287329

288330
describe('<md-chip-remove>', function() {
@@ -331,11 +373,11 @@ describe('<md-chips>', function() {
331373
' <md-chip-template><div class="mychiptemplate">[[$chip]]</div></md-chip-template>' +
332374
'</md-chips>';
333375
var element = buildChips(template);
334-
var chips = element.find('md-chip');
376+
var chips = element[0].querySelectorAll('md-chip .mychiptemplate');
335377

336-
expect(chips.eq(0).text().trim()).toEqual('Apple');
337-
expect(chips.eq(1).text().trim()).toEqual('Banana');
338-
expect(chips.eq(2).text().trim()).toEqual('Orange');
378+
expect(angular.element(chips[0]).text().trim()).toEqual('Apple');
379+
expect(angular.element(chips[1]).text().trim()).toEqual('Banana');
380+
expect(angular.element(chips[2]).text().trim()).toEqual('Orange');
339381
});
340382

341383
it('should not interpolate old-style tags in a user-provided chip template', function() {
@@ -385,6 +427,10 @@ describe('<md-chips>', function() {
385427
ctrl.inputKeydown(event);
386428
}
387429

430+
function getChipsElements(root) {
431+
return angular.element(root[0].querySelectorAll('md-chips'));
432+
}
433+
388434
function getChipElements(root) {
389435
return angular.element(root[0].querySelectorAll('md-chip'));
390436
}

src/components/chips/js/chipsDirective.js

+17-9
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
* MDChips Directive Definition
141141
*/
142142
function MdChips ($mdTheming, $mdUtil, $compile, $log, $timeout) {
143-
// Run our templates through $mdUtil.processTemplate() to allow custom start/end symbosl
143+
// Run our templates through $mdUtil.processTemplate() to allow custom start/end symbols
144144
convertTemplates();
145145

146146
return {
@@ -149,7 +149,7 @@
149149
// name with '$', Angular won't write it into the DOM. The cloned
150150
// element propagates to the link function via the attrs argument,
151151
// where various contained-elements can be consumed.
152-
var content = attrs['$mdUserTemplate'] = element.clone();
152+
attrs['$mdUserTemplate'] = element.clone();
153153
return MD_CHIPS_TEMPLATE;
154154
},
155155
require: ['mdChips'],
@@ -255,19 +255,27 @@
255255
// is complete (due to their nested nature). Wait a tick before looking for them to
256256
// configure the controller.
257257
if (chipInputTemplate != CHIP_INPUT_TEMPLATE) {
258-
$timeout(function() {
259-
if (chipInputTemplate.indexOf('<md-autocomplete') === 0)
260-
mdChipsCtrl
261-
.configureAutocomplete(element.find('md-autocomplete')
262-
.controller('mdAutocomplete'));
263-
mdChipsCtrl.configureUserInput(element.find('input'));
258+
// The autocomplete will not appear until the readonly attribute is not true (i.e.
259+
// false or undefined), so we have to watch the readonly and then on the next tick
260+
// after the chip transclusion has run, we can configure the autocomplete and user
261+
// input.
262+
scope.$watch('$mdChipsCtrl.readonly', function(readonly) {
263+
if (!readonly) {
264+
$mdUtil.nextTick(function(){
265+
if (chipInputTemplate.indexOf('<md-autocomplete') === 0)
266+
mdChipsCtrl
267+
.configureAutocomplete(element.find('md-autocomplete')
268+
.controller('mdAutocomplete'));
269+
mdChipsCtrl.configureUserInput(element.find('input'));
270+
});
271+
}
264272
});
265273
}
266274
}
267275

268276
// Compile with the parent's scope and prepend any static chips to the wrapper.
269277
if (staticChips.length > 0) {
270-
var compiledStaticChips = $compile(staticChips)(scope.$parent);
278+
var compiledStaticChips = $compile(staticChips.clone())(scope.$parent);
271279
$timeout(function() { element.find('md-chips-wrap').prepend(compiledStaticChips); });
272280
}
273281
};

0 commit comments

Comments
 (0)