From 4f868e098078911a82e45fd9a4d1ab9dd550c070 Mon Sep 17 00:00:00 2001 From: Michael Benford Date: Mon, 2 Dec 2013 00:49:49 -0200 Subject: [PATCH] feat(autocomplete): Changed tag addition behavior Changed tag addition behavior so a new tag is added when a selection is made, instead of just changing the input's value and requiring the user to manually add the tag by pressing some hotkey. Closes #30. --- src/auto-complete.js | 101 +++++++++++++++++++------------------ src/tags-input.js | 11 ++-- test/auto-complete.spec.js | 15 +++--- test/tags-input.spec.js | 13 +++-- 4 files changed, 73 insertions(+), 67 deletions(-) diff --git a/src/auto-complete.js b/src/auto-complete.js index f2424fba..140a8495 100644 --- a/src/auto-complete.js +++ b/src/auto-complete.js @@ -113,8 +113,8 @@ angular.module('tags-input').directive('autoComplete', function($document, $time maxResultsToShow: { type: Number, defaultValue: 10 } }); - suggestionList = new SuggestionList(scope.source, scope.options); tagsInput = tagsInputCtrl.registerAutocomplete(); + suggestionList = new SuggestionList(scope.source, scope.options); if (scope.options.highlightMatchedText) { highlight = function(item, text) { @@ -134,7 +134,7 @@ angular.module('tags-input').directive('autoComplete', function($document, $time var added = false; if (suggestionList.selected) { - tagsInput.changeInputValue(suggestionList.selected); + tagsInput.tryAddTag(suggestionList.selected); suggestionList.reset(); tagsInput.focusInput(); @@ -147,59 +147,60 @@ angular.module('tags-input').directive('autoComplete', function($document, $time return $sce.trustAsHtml(highlight(item, suggestionList.query)); }; - tagsInput.on('input-changed', function(value) { - if (value) { - suggestionList.load(value, tagsInput.getTags()); - } else { - suggestionList.reset(); - } - }) - .on('input-keydown', function(e) { - var key, handled; - - if (hotkeys.indexOf(e.keyCode) === -1) { - return; - } - - // This hack is needed because jqLite doesn't implement stopImmediatePropagation properly. - // I've sent a PR to Angular addressing this issue and hopefully it'll be fixed soon. - // https://github.com/angular/angular.js/pull/4833 - var immediatePropagationStopped = false; - e.stopImmediatePropagation = function() { - immediatePropagationStopped = true; - e.stopPropagation(); - }; - e.isImmediatePropagationStopped = function() { - return immediatePropagationStopped; - }; - - if (suggestionList.visible) { - key = e.keyCode; - handled = false; - - if (key === KEYS.down) { - suggestionList.selectNext(); - handled = true; - } - else if (key === KEYS.up) { - suggestionList.selectPrior(); - handled = true; - } - else if (key === KEYS.escape) { + tagsInput + .on('input-changed', function(value) { + if (value) { + suggestionList.load(value, tagsInput.getTags()); + } else { suggestionList.reset(); - handled = true; } - else if (key === KEYS.enter || key === KEYS.tab) { - handled = scope.addSuggestion(); + }) + .on('input-keydown', function(e) { + var key, handled; + + if (hotkeys.indexOf(e.keyCode) === -1) { + return; } - if (handled) { - e.preventDefault(); - e.stopImmediatePropagation(); - scope.$apply(); + // This hack is needed because jqLite doesn't implement stopImmediatePropagation properly. + // I've sent a PR to Angular addressing this issue and hopefully it'll be fixed soon. + // https://github.com/angular/angular.js/pull/4833 + var immediatePropagationStopped = false; + e.stopImmediatePropagation = function() { + immediatePropagationStopped = true; + e.stopPropagation(); + }; + e.isImmediatePropagationStopped = function() { + return immediatePropagationStopped; + }; + + if (suggestionList.visible) { + key = e.keyCode; + handled = false; + + if (key === KEYS.down) { + suggestionList.selectNext(); + handled = true; + } + else if (key === KEYS.up) { + suggestionList.selectPrior(); + handled = true; + } + else if (key === KEYS.escape) { + suggestionList.reset(); + handled = true; + } + else if (key === KEYS.enter || key === KEYS.tab) { + handled = scope.addSuggestion(); + } + + if (handled) { + e.preventDefault(); + e.stopImmediatePropagation(); + scope.$apply(); + } } - } - }); + }); $document.on('click', function() { if (suggestionList.visible) { diff --git a/src/tags-input.js b/src/tags-input.js index a6bf6267..ccb03006 100644 --- a/src/tags-input.js +++ b/src/tags-input.js @@ -169,18 +169,19 @@ angular.module('tags-input').directive('tagsInput', function(configuration) { }; return { - changeInputValue: function(value) { - $scope.newTag = value; + tryAddTag: function(tag) { + $scope.newTag = tag; + return $scope.tryAdd(); }, focusInput: function() { input[0].focus(); }, + getTags: function() { + return $scope.tags; + }, on: function(name, handler) { events.on(name, handler); return this; - }, - getTags: function() { - return $scope.tags; } }; }; diff --git a/test/auto-complete.spec.js b/test/auto-complete.spec.js index a819d788..8747a0cf 100644 --- a/test/auto-complete.spec.js +++ b/test/auto-complete.spec.js @@ -27,6 +27,7 @@ describe('autocomplete-directive', function() { tagsInput = { changeInputValue: jasmine.createSpy(), + tryAddTag: jasmine.createSpy(), focusInput: jasmine.createSpy(), on: jasmine.createSpy().andCallFake(function(name, handler) { eventHandlers[name] = handler; @@ -175,7 +176,7 @@ describe('autocomplete-directive', function() { expect(isSuggestionsBoxVisible()).toBe(false); }); - it('adds the selected suggestion to the input field when the enter key is pressed and the suggestions box is visible', function() { + it('adds the selected suggestion when the enter key is pressed and the suggestions box is visible', function() { // Arrange loadSuggestions(['Item1', 'Item2']); suggestionList.select(0); @@ -184,10 +185,10 @@ describe('autocomplete-directive', function() { sendKeyDown(KEYS.enter); // Assert - expect(tagsInput.changeInputValue).toHaveBeenCalledWith('Item1'); + expect(tagsInput.tryAddTag).toHaveBeenCalledWith('Item1'); }); - it('adds the selected suggestion to the input field when the tab key is pressed and there is a suggestion selected', function() { + it('adds the selected suggestion when the tab key is pressed and there is a suggestion selected', function() { // Arrange loadSuggestions(['Item1', 'Item2']); suggestionList.select(0); @@ -196,7 +197,7 @@ describe('autocomplete-directive', function() { sendKeyDown(KEYS.tab); // Assert - expect(tagsInput.changeInputValue).toHaveBeenCalledWith('Item1'); + expect(tagsInput.tryAddTag).toHaveBeenCalledWith('Item1'); }); it('does not change the input value when the enter key is pressed and there is nothing selected', function() { @@ -207,7 +208,7 @@ describe('autocomplete-directive', function() { sendKeyDown(KEYS.enter); // Assert - expect(tagsInput.changeInputValue).not.toHaveBeenCalled(); + expect(tagsInput.tryAddTag).not.toHaveBeenCalled(); }); it('sets the selected suggestion to null after adding it to the input field', function() { @@ -322,7 +323,7 @@ describe('autocomplete-directive', function() { expect(suggestionList.selected).toBe('Item2'); }); - it('adds the selected suggestion to the input field when a mouse click is triggered', function() { + it('adds the selected suggestion when a mouse click is triggered', function() { // Arrange loadSuggestions(['Item1', 'Item2', 'Item3']); getSuggestion(1).mouseenter(); @@ -331,7 +332,7 @@ describe('autocomplete-directive', function() { getSuggestion(1).click(); // Assert - expect(tagsInput.changeInputValue).toHaveBeenCalledWith('Item2'); + expect(tagsInput.tryAddTag).toHaveBeenCalledWith('Item2'); }); it('focuses the input field when a suggestion is added via a mouse click', function() { diff --git a/test/tags-input.spec.js b/test/tags-input.spec.js index 2f177a54..65ba7e9f 100644 --- a/test/tags-input.spec.js +++ b/test/tags-input.spec.js @@ -764,20 +764,23 @@ describe('tags-input-directive', function() { it('creates an object containing all the autocomplete directive needs to work', function() { expect(autocompleteObj).toEqual({ - changeInputValue: jasmine.any(Function), + tryAddTag: jasmine.any(Function), focusInput: jasmine.any(Function), on: jasmine.any(Function), getTags: jasmine.any(Function) }); }); - it('changes the input value', function() { - // Act - autocompleteObj.changeInputValue('ABC'); + it('tries to add a tag', function() { + // Arrange + $scope.tags = []; $scope.$digest(); + // Act + autocompleteObj.tryAddTag('tag'); + // Assert - expect(getInput().val()).toBe('ABC'); + expect($scope.tags).toEqual(['tag']); }); it('focus the input box', function() {