diff --git a/src/components/chips/chips-theme.scss b/src/components/chips/chips-theme.scss
index 1d8d62f7ae..fcbd92c74a 100644
--- a/src/components/chips/chips-theme.scss
+++ b/src/components/chips/chips-theme.scss
@@ -31,6 +31,11 @@ md-chips.md-THEME_NAME-theme {
color: '{{primary-contrast}}';
}
}
+
+ &._md-chip-editing {
+ background: transparent;
+ color: '{{background-800}}';
+ }
}
md-chip-remove {
.md-button {
diff --git a/src/components/chips/chips.scss b/src/components/chips/chips.scss
index 90888665bb..1f367593cb 100644
--- a/src/components/chips/chips.scss
+++ b/src/components/chips/chips.scss
@@ -100,6 +100,12 @@ $contact-chip-name-width: rem(12) !default;
outline: none;
}
}
+ &._md-chip-content-edit-is-enabled {
+ -webkit-user-select: none; /* webkit (safari, chrome) browsers */
+ -moz-user-select: none; /* mozilla browsers */
+ -khtml-user-select: none; /* webkit (konqueror) browsers */
+ -ms-user-select: none; /* IE10+ */
+ }
._md-chip-remove-container {
position: absolute;
@include rtl-prop(right, left, 0);
diff --git a/src/components/chips/demoBasicUsage/index.html b/src/components/chips/demoBasicUsage/index.html
index 9b1119008c..43be5e8232 100644
--- a/src/components/chips/demoBasicUsage/index.html
+++ b/src/components/chips/demoBasicUsage/index.html
@@ -22,6 +22,12 @@
Use the default chip template.
+
+
+ Make chips editable.
+
+
+
Use Placeholders and override hint texts.
diff --git a/src/components/chips/demoBasicUsage/script.js b/src/components/chips/demoBasicUsage/script.js
index f4e960ef2d..d86a107ecd 100644
--- a/src/components/chips/demoBasicUsage/script.js
+++ b/src/components/chips/demoBasicUsage/script.js
@@ -12,6 +12,8 @@
// Lists of fruit names and Vegetable objects
self.fruitNames = ['Apple', 'Banana', 'Orange'];
self.roFruitNames = angular.copy(self.fruitNames);
+ self.editableFruitNames = angular.copy(self.fruitNames);
+
self.tags = [];
self.vegObjs = [
{
diff --git a/src/components/chips/js/chipController.js b/src/components/chips/js/chipController.js
new file mode 100644
index 0000000000..c6b4d9b055
--- /dev/null
+++ b/src/components/chips/js/chipController.js
@@ -0,0 +1,192 @@
+angular
+ .module('material.components.chips')
+ .controller('MdChipCtrl', MdChipCtrl);
+
+/**
+ * Controller for the MdChip component. Responsible for handling keyboard
+ * events and editting the chip if needed.
+ *
+ * @param $scope
+ * @param $element
+ * @param $mdConstant
+ * @param $timeout
+ * @param $mdUtil
+ * @constructor
+ */
+function MdChipCtrl ($scope, $element, $mdConstant, $timeout, $mdUtil) {
+ /**
+ * @type {$scope}
+ */
+ this.$scope = $scope;
+
+ /**
+ * @type {$element}
+ */
+ this.$element = $element;
+
+ /**
+ * @type {$mdConstant}
+ */
+ this.$mdConstant = $mdConstant;
+
+ /**
+ * @type {$timeout}
+ */
+ this.$timeout = $timeout;
+
+ /**
+ * @type {$mdUtil}
+ */
+ this.$mdUtil = $mdUtil;
+
+ /**
+ * @type {boolean}
+ */
+ this.isEditting = false;
+
+ /**
+ * @type {MdChipsCtrl}
+ */
+ this.parentController = undefined;
+
+ /**
+ * @type {boolean}
+ */
+ this.enableChipEdit = false;
+}
+
+
+/**
+ * @param {MdChipsCtrl} controller
+ */
+MdChipCtrl.prototype.init = function(controller) {
+ this.parentController = controller;
+ this.enableChipEdit = this.parentController.enableChipEdit;
+
+ if (this.enableChipEdit) {
+ this.$element.on('keydown', this.chipKeyDown.bind(this));
+ this.$element.on('mousedown', this.chipMouseDown.bind(this));
+ this.getChipContent().addClass('_md-chip-content-edit-is-enabled');
+ }
+};
+
+
+/**
+ * @return {Object}
+ */
+MdChipCtrl.prototype.getChipContent = function() {
+ var chipContents = this.$element[0].getElementsByClassName('_md-chip-content');
+ return angular.element(chipContents[0]);
+};
+
+
+/**
+ * @return {Object}
+ */
+MdChipCtrl.prototype.getContentElement = function() {
+ return angular.element(this.getChipContent().children()[0]);
+};
+
+
+/**
+ * @return {number}
+ */
+MdChipCtrl.prototype.getChipIndex = function() {
+ return parseInt(this.$element.attr('index'));
+};
+
+
+/**
+ * Presents an input element to edit the contents of the chip.
+ */
+MdChipCtrl.prototype.goOutOfEditMode = function() {
+ if (!this.isEditting) return;
+
+ this.isEditting = false;
+ this.$element.removeClass('_md-chip-editing');
+ this.getChipContent()[0].contentEditable = 'false';
+ var chipIndex = this.getChipIndex();
+
+ var content = this.getContentElement().text();
+ if (content) {
+ this.parentController.updateChipContents(
+ chipIndex,
+ this.getContentElement().text()
+ );
+
+ this.$mdUtil.nextTick(function() {
+ if (this.parentController.selectedChip === chipIndex) {
+ this.parentController.focusChip(chipIndex);
+ }
+ }.bind(this));
+ } else {
+ this.parentController.removeChipAndFocusInput(chipIndex);
+ }
+};
+
+
+/**
+ * Given an HTML element. Selects contents of it.
+ * @param node
+ */
+MdChipCtrl.prototype.selectNodeContents = function(node) {
+ var range, selection;
+ if (document.body.createTextRange) {
+ range = document.body.createTextRange();
+ range.moveToElementText(node);
+ range.select();
+ } else if (window.getSelection) {
+ selection = window.getSelection();
+ range = document.createRange();
+ range.selectNodeContents(node);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+};
+
+
+/**
+ * Presents an input element to edit the contents of the chip.
+ */
+MdChipCtrl.prototype.goInEditMode = function() {
+ this.isEditting = true;
+ this.$element.addClass('_md-chip-editing');
+ this.getChipContent()[0].contentEditable = 'true';
+ this.getChipContent().on('blur', function() {
+ this.goOutOfEditMode();
+ }.bind(this));
+
+ this.selectNodeContents(this.getChipContent()[0]);
+};
+
+
+/**
+ * Handles the keydown event on the chip element. If enable-chip-edit attribute is
+ * set to true, space or enter keys can trigger going into edit mode. Enter can also
+ * trigger submitting if the chip is already being edited.
+ * @param event
+ */
+MdChipCtrl.prototype.chipKeyDown = function(event) {
+ if (!this.isEditting &&
+ (event.keyCode === this.$mdConstant.KEY_CODE.ENTER ||
+ event.keyCode === this.$mdConstant.KEY_CODE.SPACE)) {
+ event.preventDefault();
+ this.goInEditMode();
+ } else if (this.isEditting &&
+ event.keyCode === this.$mdConstant.KEY_CODE.ENTER) {
+ event.preventDefault();
+ this.goOutOfEditMode();
+ }
+};
+
+
+/**
+ * Handles the double click event
+ */
+MdChipCtrl.prototype.chipMouseDown = function() {
+ if(this.getChipIndex() == this.parentController.selectedChip &&
+ this.enableChipEdit &&
+ !this.isEditting) {
+ this.goInEditMode();
+ }
+};
diff --git a/src/components/chips/js/chipDirective.js b/src/components/chips/js/chipDirective.js
index b934886ba2..607e7f18a1 100644
--- a/src/components/chips/js/chipDirective.js
+++ b/src/components/chips/js/chipDirective.js
@@ -30,7 +30,7 @@ var DELETE_HINT_TEMPLATE = '\
* MDChip Directive Definition
*
* @param $mdTheming
- * @param $mdInkRipple
+ * @param $mdUtil
* @ngInject
*/
function MdChip($mdTheming, $mdUtil) {
@@ -38,22 +38,29 @@ function MdChip($mdTheming, $mdUtil) {
return {
restrict: 'E',
- require: '^?mdChips',
- compile: compile
+ require: ['^?mdChips', 'mdChip'],
+ compile: compile,
+ controller: 'MdChipCtrl'
};
function compile(element, attr) {
// Append the delete template
element.append($mdUtil.processTemplate(hintTemplate));
- return function postLink(scope, element, attr, ctrl) {
+ return function postLink(scope, element, attr, ctrls) {
+ var chipsController = ctrls.shift();
+ var chipController = ctrls.shift();
$mdTheming(element);
- if (ctrl) angular.element(element[0].querySelector('._md-chip-content'))
- .on('blur', function () {
- ctrl.resetSelectedChip();
- ctrl.$scope.$applyAsync();
- });
+ if (chipsController) {
+ chipController.init(chipsController);
+
+ angular.element(element[0].querySelector('._md-chip-content'))
+ .on('blur', function () {
+ chipsController.selectedChip = -1;
+ chipsController.$scope.$applyAsync();
+ });
+ }
};
}
}
diff --git a/src/components/chips/js/chipsController.js b/src/components/chips/js/chipsController.js
old mode 100755
new mode 100644
index d19558e38b..bf055a1e9d
--- a/src/components/chips/js/chipsController.js
+++ b/src/components/chips/js/chipsController.js
@@ -11,9 +11,10 @@ angular
* @param $mdConstant
* @param $log
* @param $element
+ * @param $mdUtil
* @constructor
*/
-function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout) {
+function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout, $mdUtil) {
/** @type {$timeout} **/
this.$timeout = $timeout;
@@ -50,6 +51,8 @@ function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout) {
/** @type {boolean} */
this.hasAutocomplete = false;
+ /** @type {string} */
+ this.enableChipEdit = $mdUtil.parseAttributeBoolean(this.mdEnableChipEdit);
/**
* Hidden hint text for how to delete a chip. Used to give context to screen readers.
@@ -146,6 +149,29 @@ MdChipsCtrl.prototype.inputKeydown = function(event) {
}
};
+
+/**
+ * Updates the content of the chip at given index
+ * @param chipIndex
+ * @param chipContents
+ */
+MdChipsCtrl.prototype.updateChipContents = function(chipIndex, chipContents){
+ if(chipIndex >= 0 && chipIndex < this.items.length) {
+ this.items[chipIndex] = chipContents;
+ this.ngModelCtrl.$setDirty();
+ }
+};
+
+
+/**
+ * Returns true if a chip is currently being edited. False otherwise.
+ * @return {boolean}
+ */
+MdChipsCtrl.prototype.isEditingChip = function(){
+ return !!this.$element[0].getElementsByClassName('_md-chip-editing').length;
+};
+
+
/**
* Handles the keydown event on the chip elements: backspace removes the selected chip, arrow
* keys switch which chips is active
@@ -153,6 +179,8 @@ MdChipsCtrl.prototype.inputKeydown = function(event) {
*/
MdChipsCtrl.prototype.chipKeydown = function (event) {
if (this.getChipBuffer()) return;
+ if (this.isEditingChip()) return;
+
switch (event.keyCode) {
case this.$mdConstant.KEY_CODE.BACKSPACE:
case this.$mdConstant.KEY_CODE.DELETE:
diff --git a/src/components/chips/js/chipsDirective.js b/src/components/chips/js/chipsDirective.js
old mode 100755
new mode 100644
index 5260fc6d09..4a1987fd60
--- a/src/components/chips/js/chipsDirective.js
+++ b/src/components/chips/js/chipsDirective.js
@@ -68,6 +68,9 @@
* @param {boolean=} readonly Disables list manipulation (deleting or adding list items), hiding
* the input and delete buttons. If no `ng-model` is provided, the chips will automatically be
* marked as readonly.
+ * @param {string=} md-enable-chip-edit Set this to "true" to enable editing of chip contents. The user can
+ * go into edit mode with pressing "space", "enter", or double clicking on the chip. Chip edit is only
+ * supported for chips with basic template.
* @param {number=} md-max-chips The maximum number of chips allowed to add through user input.
*
The validation property `md-max-chips` can be used when the max chips
* amount is reached.
@@ -195,6 +198,7 @@
scope: {
readonly: '=readonly',
placeholder: '@',
+ mdEnableChipEdit: '@',
secondaryPlaceholder: '@',
maxChips: '@mdMaxChips',
transformChip: '&mdTransformChip',