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

Commit 0563f24

Browse files
Splaktarmmalerba
authored andcommitted
feat(chips): trigger ng-change on chip addition/removal (#11237)
* Add test of `ng-change` for `md-chips` * Add docs regarding `ng-change` for `md-chips` and `md-contact-chips` * Add demo for ng-change on `md-chips` * Add demo for ng-change on `md-contact-chips` Closes #11161 Closes #3857
1 parent 5111f9d commit 0563f24

File tree

8 files changed

+55
-9
lines changed

8 files changed

+55
-9
lines changed

src/components/chips/chips.spec.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ describe('<md-chips>', function() {
1212
'<md-chips ng-model="items" md-on-remove="removeChip($chip, $index, $event)"></md-chips>';
1313
var CHIP_SELECT_TEMPLATE =
1414
'<md-chips ng-model="items" md-on-select="selectChip($chip)"></md-chips>';
15+
var CHIP_NG_CHANGE_TEMPLATE =
16+
'<md-chips ng-model="items" ng-change="onModelChange(items)"></md-chips>';
1517
var CHIP_READONLY_TEMPLATE =
1618
'<md-chips ng-model="items" readonly="isReadonly"></md-chips>';
1719
var CHIP_READONLY_AUTOCOMPLETE_TEMPLATE =
@@ -204,6 +206,27 @@ describe('<md-chips>', function() {
204206
expect(scope.removeChip.calls.mostRecent().args[2].type).toBe('click');
205207
});
206208

209+
it('should trigger ng-change on chip addition/removal', function() {
210+
var element = buildChips(CHIP_NG_CHANGE_TEMPLATE);
211+
var ctrl = element.controller('mdChips');
212+
213+
scope.onModelChange = jasmine.createSpy('onModelChange');
214+
215+
element.scope().$apply(function() {
216+
ctrl.chipBuffer = 'Melon';
217+
simulateInputEnterKey(ctrl);
218+
});
219+
expect(scope.onModelChange).toHaveBeenCalled();
220+
expect(scope.onModelChange.calls.mostRecent().args[0].length).toBe(4);
221+
222+
element.scope().$apply(function() {
223+
ctrl.removeChip(0);
224+
});
225+
expect(scope.onModelChange).toHaveBeenCalled();
226+
expect(scope.onModelChange.calls.mostRecent().args[0].length).toBe(3);
227+
});
228+
229+
207230
it('should call the select method when selecting a chip', function() {
208231
var element = buildChips(CHIP_SELECT_TEMPLATE);
209232
var ctrl = element.controller('mdChips');
@@ -1686,7 +1709,7 @@ describe('<md-chips>', function() {
16861709
return scope.fruits.filter(function(item) {
16871710
return item.toLowerCase().indexOf(searchText.toLowerCase()) === 0;
16881711
});
1689-
}
1712+
};
16901713
}
16911714

16921715
function simulateInputEnterKey(ctrl) {

src/components/chips/demoBasicUsage/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ <h2 class="md-title">Use the default chip template.</h2>
2222

2323
<md-chips ng-model="ctrl.fruitNames" readonly="ctrl.readonly" md-removable="ctrl.removable"></md-chips>
2424

25+
<br/>
26+
<h2 class="md-title">Use ng-change</h2>
27+
28+
<md-chips ng-model="ctrl.ngChangeFruitNames" ng-change="ctrl.onModelChange(ctrl.ngChangeFruitNames)"
29+
md-removable="ctrl.removable"></md-chips>
2530

2631
<br/>
2732
<h2 class="md-title">Make chips editable.</h2>

src/components/chips/demoBasicUsage/script.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
// Lists of fruit names and Vegetable objects
1616
self.fruitNames = ['Apple', 'Banana', 'Orange'];
17+
self.ngChangeFruitNames = angular.copy(self.fruitNames);
1718
self.roFruitNames = angular.copy(self.fruitNames);
1819
self.editableFruitNames = angular.copy(self.fruitNames);
1920

@@ -39,5 +40,9 @@
3940
type: 'unknown'
4041
};
4142
};
43+
44+
self.onModelChange = function(newModel) {
45+
alert('The model has changed');
46+
};
4247
}
4348
})();

src/components/chips/demoContactChips/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<md-content class="md-padding autocomplete" layout="column">
44
<md-contact-chips
55
ng-model="ctrl.contacts"
6+
ng-change="ctrl.onModelChange(ctrl.contacts)"
67
md-contacts="ctrl.querySearch($query)"
78
md-contact-name="name"
89
md-contact-image="image"

src/components/chips/demoContactChips/script.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
self.querySearch = querySearch;
2727
self.delayedQuerySearch = delayedQuerySearch;
28+
self.onModelChange = onModelChange;
2829

2930
/**
3031
* Search for contacts; use a random delay to simulate a remote call
@@ -84,6 +85,10 @@
8485

8586
}
8687

88+
function onModelChange(model) {
89+
alert('The model has changed');
90+
}
91+
8792
function loadContacts() {
8893
var contacts = [
8994
'Marina Augustine',

src/components/chips/js/chipsController.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ MdChipsCtrl.prototype.getCursorPosition = function(element) {
328328
MdChipsCtrl.prototype.updateChipContents = function(chipIndex, chipContents){
329329
if(chipIndex >= 0 && chipIndex < this.items.length) {
330330
this.items[chipIndex] = chipContents;
331-
this.ngModelCtrl.$setDirty();
331+
this.updateNgModel();
332332
}
333333
};
334334

@@ -484,9 +484,7 @@ MdChipsCtrl.prototype.appendChip = function(newChip) {
484484
var length = this.items.push(newChip);
485485
var index = length - 1;
486486

487-
// Update model validation
488-
this.ngModelCtrl.$setDirty();
489-
this.validateModel();
487+
this.updateNgModel();
490488

491489
// If they provide the md-on-add attribute, notify them of the chip addition
492490
if (this.useOnAdd && this.onAdd) {
@@ -585,6 +583,13 @@ MdChipsCtrl.prototype.validateModel = function() {
585583
this.ngModelCtrl.$validate(); // rerun any registered validators
586584
};
587585

586+
MdChipsCtrl.prototype.updateNgModel = function() {
587+
this.ngModelCtrl.$setViewValue(this.items.slice());
588+
// TODO add the md-max-chips validator to this.ngModelCtrl.validators so that
589+
// the validation will be performed automatically on $viewValue change
590+
this.validateModel();
591+
};
592+
588593
/**
589594
* Removes the chip at the given index.
590595
* @param {number} index
@@ -593,9 +598,7 @@ MdChipsCtrl.prototype.validateModel = function() {
593598
MdChipsCtrl.prototype.removeChip = function(index, event) {
594599
var removed = this.items.splice(index, 1);
595600

596-
// Update model validation
597-
this.ngModelCtrl.$setDirty();
598-
this.validateModel();
601+
this.updateNgModel();
599602

600603
if (removed && removed.length && this.useOnRemove && this.onRemove) {
601604
this.onRemove({ '$chip': removed[0], '$index': index, '$event': event });

src/components/chips/js/chipsDirective.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@
9696
*
9797
* Please refer to the documentation of this option (below) for more information.
9898
*
99-
* @param {string|object=} ng-model A model to which the list of items will be bound.
99+
* @param {string=|object=} ng-model A model to which the list of items will be bound.
100+
* @param {expression=} ng-change AngularJS expression to be executed on chip addition/removal.
100101
* @param {string=} placeholder Placeholder text that will be forwarded to the input.
101102
* @param {string=} secondary-placeholder Placeholder text that will be forwarded to the input,
102103
* displayed when there is at least one item in the list

src/components/chips/js/contactChipsDirective.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ angular
1717
* appearance of the matched text inside of the contacts' autocomplete popup.
1818
*
1919
* @param {string=|object=} ng-model A model to bind the list of items to
20+
* @param {expression=} ng-change AngularJS expression to be executed on chip addition/removal
2021
* @param {string=} placeholder Placeholder text that will be forwarded to the input.
2122
* @param {string=} secondary-placeholder Placeholder text that will be forwarded to the input,
2223
* displayed when there is at least on item in the list
@@ -57,6 +58,7 @@ angular
5758
var MD_CONTACT_CHIPS_TEMPLATE = '\
5859
<md-chips class="md-contact-chips"\
5960
ng-model="$mdContactChipsCtrl.contacts"\
61+
ng-change="$mdContactChipsCtrl.ngChange($mdContactChipsCtrl.contacts)"\
6062
md-require-match="$mdContactChipsCtrl.requireMatch"\
6163
md-chip-append-delay="{{$mdContactChipsCtrl.chipAppendDelay}}" \
6264
md-autocomplete-snap>\
@@ -122,6 +124,7 @@ function MdContactChips($mdTheming, $mdUtil) {
122124
contactImage: '@mdContactImage',
123125
contactEmail: '@mdContactEmail',
124126
contacts: '=ngModel',
127+
ngChange: '&',
125128
requireMatch: '=?mdRequireMatch',
126129
minLength: '=?mdMinLength',
127130
highlightFlags: '@?mdHighlightFlags',

0 commit comments

Comments
 (0)