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

fix(mdChips): Fix error with audocomplete inside readonly chips and allow usage inside ng-repeat. #4133

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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