diff --git a/src/components/chips/chips.spec.js b/src/components/chips/chips.spec.js index a39095054c..2ecbbad66c 100755 --- a/src/components/chips/chips.spec.js +++ b/src/components/chips/chips.spec.js @@ -12,6 +12,8 @@ describe('', function() { ''; var CHIP_SELECT_TEMPLATE = ''; + var CHIP_NG_CHANGE_TEMPLATE = + ''; var CHIP_READONLY_TEMPLATE = ''; var CHIP_READONLY_AUTOCOMPLETE_TEMPLATE = @@ -204,6 +206,27 @@ describe('', function() { expect(scope.removeChip.calls.mostRecent().args[2].type).toBe('click'); }); + it('should trigger ng-change on chip addition/removal', function() { + var element = buildChips(CHIP_NG_CHANGE_TEMPLATE); + var ctrl = element.controller('mdChips'); + + scope.onModelChange = jasmine.createSpy('onModelChange'); + + element.scope().$apply(function() { + ctrl.chipBuffer = 'Melon'; + simulateInputEnterKey(ctrl); + }); + expect(scope.onModelChange).toHaveBeenCalled(); + expect(scope.onModelChange.calls.mostRecent().args[0].length).toBe(4); + + element.scope().$apply(function() { + ctrl.removeChip(0); + }); + expect(scope.onModelChange).toHaveBeenCalled(); + expect(scope.onModelChange.calls.mostRecent().args[0].length).toBe(3); + }); + + it('should call the select method when selecting a chip', function() { var element = buildChips(CHIP_SELECT_TEMPLATE); var ctrl = element.controller('mdChips'); @@ -1614,7 +1637,7 @@ describe('', function() { return scope.fruits.filter(function(item) { return item.toLowerCase().indexOf(searchText.toLowerCase()) === 0; }); - } + }; } function simulateInputEnterKey(ctrl) { diff --git a/src/components/chips/demoBasicUsage/index.html b/src/components/chips/demoBasicUsage/index.html index d5cb427983..6c0c5b2984 100644 --- a/src/components/chips/demoBasicUsage/index.html +++ b/src/components/chips/demoBasicUsage/index.html @@ -22,6 +22,11 @@

Use the default chip template.

+
+

Use ng-change

+ +

Make chips editable.

diff --git a/src/components/chips/demoBasicUsage/script.js b/src/components/chips/demoBasicUsage/script.js index 8425e4acb0..7f615e520a 100644 --- a/src/components/chips/demoBasicUsage/script.js +++ b/src/components/chips/demoBasicUsage/script.js @@ -14,6 +14,7 @@ // Lists of fruit names and Vegetable objects self.fruitNames = ['Apple', 'Banana', 'Orange']; + self.ngChangeFruitNames = angular.copy(self.fruitNames); self.roFruitNames = angular.copy(self.fruitNames); self.editableFruitNames = angular.copy(self.fruitNames); @@ -39,5 +40,9 @@ type: 'unknown' }; }; + + self.onModelChange = function(newModel) { + alert('The model has changed'); + }; } })(); diff --git a/src/components/chips/demoContactChips/index.html b/src/components/chips/demoContactChips/index.html index a0fbcad628..f188303429 100644 --- a/src/components/chips/demoContactChips/index.html +++ b/src/components/chips/demoContactChips/index.html @@ -3,6 +3,7 @@ = 0 && chipIndex < this.items.length) { this.items[chipIndex] = chipContents; - this.ngModelCtrl.$setDirty(); + this.updateNgModel(); } }; @@ -463,9 +463,7 @@ MdChipsCtrl.prototype.appendChip = function(newChip) { var length = this.items.push(newChip); var index = length - 1; - // Update model validation - this.ngModelCtrl.$setDirty(); - this.validateModel(); + this.updateNgModel(); // If they provide the md-on-add attribute, notify them of the chip addition if (this.useOnAdd && this.onAdd) { @@ -563,6 +561,13 @@ MdChipsCtrl.prototype.validateModel = function() { this.ngModelCtrl.$setValidity('md-max-chips', !this.hasMaxChipsReached()); }; +MdChipsCtrl.prototype.updateNgModel = function() { + this.ngModelCtrl.$setViewValue(this.items.slice()); + // TODO add the md-max-chips validator to this.ngModelCtrl.validators so that + // the validation will be performed automatically on $viewValue change + this.validateModel(); +}; + /** * Removes the chip at the given index. * @param {number} index @@ -571,9 +576,7 @@ MdChipsCtrl.prototype.validateModel = function() { MdChipsCtrl.prototype.removeChip = function(index, event) { var removed = this.items.splice(index, 1); - // Update model validation - this.ngModelCtrl.$setDirty(); - this.validateModel(); + this.updateNgModel(); if (removed && removed.length && this.useOnRemove && this.onRemove) { this.onRemove({ '$chip': removed[0], '$index': index, '$event': event }); diff --git a/src/components/chips/js/chipsDirective.js b/src/components/chips/js/chipsDirective.js index 2e7700489a..442f19fe36 100644 --- a/src/components/chips/js/chipsDirective.js +++ b/src/components/chips/js/chipsDirective.js @@ -96,7 +96,8 @@ * * Please refer to the documentation of this option (below) for more information. * - * @param {string|object=} ng-model A model to which the list of items will be bound. + * @param {string=|object=} ng-model A model to which the list of items will be bound. + * @param {expression=} ng-change AngularJS expression to be executed on chip addition/removal. * @param {string=} placeholder Placeholder text that will be forwarded to the input. * @param {string=} secondary-placeholder Placeholder text that will be forwarded to the input, * displayed when there is at least one item in the list diff --git a/src/components/chips/js/contactChipsDirective.js b/src/components/chips/js/contactChipsDirective.js index 20ef2480df..081bcb6069 100644 --- a/src/components/chips/js/contactChipsDirective.js +++ b/src/components/chips/js/contactChipsDirective.js @@ -17,6 +17,7 @@ angular * appearance of the matched text inside of the contacts' autocomplete popup. * * @param {string=|object=} ng-model A model to bind the list of items to + * @param {expression=} ng-change AngularJS expression to be executed on chip addition/removal * @param {string=} placeholder Placeholder text that will be forwarded to the input. * @param {string=} secondary-placeholder Placeholder text that will be forwarded to the input, * displayed when there is at least on item in the list @@ -57,6 +58,7 @@ angular var MD_CONTACT_CHIPS_TEMPLATE = '\ \ @@ -122,6 +124,7 @@ function MdContactChips($mdTheming, $mdUtil) { contactImage: '@mdContactImage', contactEmail: '@mdContactEmail', contacts: '=ngModel', + ngChange: '&', requireMatch: '=?mdRequireMatch', minLength: '=?mdMinLength', highlightFlags: '@?mdHighlightFlags',