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

feat(chips): trigger ng-change on chip addition/removal #11237

Merged
merged 1 commit into from
Apr 17, 2018
Merged
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
25 changes: 24 additions & 1 deletion src/components/chips/chips.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ describe('<md-chips>', function() {
'<md-chips ng-model="items" md-on-remove="removeChip($chip, $index, $event)"></md-chips>';
var CHIP_SELECT_TEMPLATE =
'<md-chips ng-model="items" md-on-select="selectChip($chip)"></md-chips>';
var CHIP_NG_CHANGE_TEMPLATE =
'<md-chips ng-model="items" ng-change="onModelChange(items)"></md-chips>';
var CHIP_READONLY_TEMPLATE =
'<md-chips ng-model="items" readonly="isReadonly"></md-chips>';
var CHIP_READONLY_AUTOCOMPLETE_TEMPLATE =
Expand Down Expand Up @@ -204,6 +206,27 @@ describe('<md-chips>', 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');
Expand Down Expand Up @@ -1614,7 +1637,7 @@ describe('<md-chips>', function() {
return scope.fruits.filter(function(item) {
return item.toLowerCase().indexOf(searchText.toLowerCase()) === 0;
});
}
};
}

function simulateInputEnterKey(ctrl) {
Expand Down
5 changes: 5 additions & 0 deletions src/components/chips/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ <h2 class="md-title">Use the default chip template.</h2>

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

<br/>
<h2 class="md-title">Use ng-change</h2>

<md-chips ng-model="ctrl.ngChangeFruitNames" ng-change="ctrl.onModelChange(ctrl.ngChangeFruitNames)"
md-removable="ctrl.removable"></md-chips>

<br/>
<h2 class="md-title">Make chips editable.</h2>
Expand Down
5 changes: 5 additions & 0 deletions src/components/chips/demoBasicUsage/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -39,5 +40,9 @@
type: 'unknown'
};
};

self.onModelChange = function(newModel) {
alert('The model has changed');
};
}
})();
1 change: 1 addition & 0 deletions src/components/chips/demoContactChips/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<md-content class="md-padding autocomplete" layout="column">
<md-contact-chips
ng-model="ctrl.contacts"
ng-change="ctrl.onModelChange(ctrl.contacts)"
md-contacts="ctrl.querySearch($query)"
md-contact-name="name"
md-contact-image="image"
Expand Down
5 changes: 5 additions & 0 deletions src/components/chips/demoContactChips/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

self.querySearch = querySearch;
self.delayedQuerySearch = delayedQuerySearch;
self.onModelChange = onModelChange;

/**
* Search for contacts; use a random delay to simulate a remote call
Expand Down Expand Up @@ -84,6 +85,10 @@

}

function onModelChange(model) {
alert('The model has changed');
}

function loadContacts() {
var contacts = [
'Marina Augustine',
Expand Down
17 changes: 10 additions & 7 deletions src/components/chips/js/chipsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ MdChipsCtrl.prototype.getCursorPosition = function(element) {
MdChipsCtrl.prototype.updateChipContents = function(chipIndex, chipContents){
if(chipIndex >= 0 && chipIndex < this.items.length) {
this.items[chipIndex] = chipContents;
this.ngModelCtrl.$setDirty();
this.updateNgModel();
}
};

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -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 });
Expand Down
3 changes: 2 additions & 1 deletion src/components/chips/js/chipsDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/components/chips/js/contactChipsDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -57,6 +58,7 @@ angular
var MD_CONTACT_CHIPS_TEMPLATE = '\
<md-chips class="md-contact-chips"\
ng-model="$mdContactChipsCtrl.contacts"\
ng-change="$mdContactChipsCtrl.ngChange($mdContactChipsCtrl.contacts)"\
md-require-match="$mdContactChipsCtrl.requireMatch"\
md-chip-append-delay="{{$mdContactChipsCtrl.chipAppendDelay}}" \
md-autocomplete-snap>\
Expand Down Expand Up @@ -122,6 +124,7 @@ function MdContactChips($mdTheming, $mdUtil) {
contactImage: '@mdContactImage',
contactEmail: '@mdContactEmail',
contacts: '=ngModel',
ngChange: '&',
requireMatch: '=?mdRequireMatch',
minLength: '=?mdMinLength',
highlightFlags: '@?mdHighlightFlags',
Expand Down