Skip to content
This repository has been archived by the owner on Nov 22, 2021. It is now read-only.

Commit

Permalink
fix(tagsInput): Set element's validity when options change
Browse files Browse the repository at this point in the history
Set element's validity when minTags, maxTags and allowLeftoverText
options change. Other validation options don't need to be monitored
because they are only used upon some user interaction with the directive.

Closes #154
  • Loading branch information
mbenford committed Jul 6, 2014
1 parent 8301308 commit e89f268
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ tagsInput.provider('tagsInputConfig', function() {
if (interpolationStatus[directive] && interpolationStatus[directive][key]) {
attrs.$observe(key, function(value) {
updateValue(value);
scope.events.trigger('option-change', { name: key, newValue: value });
});
}
else {
Expand Down
25 changes: 19 additions & 6 deletions src/tags-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ tagsInput.directive('tagsInput', function($timeout, $document, tagsInputConfig)
transclude: true,
templateUrl: 'ngTagsInput/tags-input.html',
controller: function($scope, $attrs, $element) {
$scope.events = new SimplePubSub();

tagsInputConfig.load('tagsInput', $scope, $attrs, {
placeholder: [String, 'Add a tag'],
tabindex: [Number],
Expand All @@ -138,7 +140,6 @@ tagsInput.directive('tagsInput', function($timeout, $document, tagsInputConfig)
addFromAutocompleteOnly: [Boolean, false]
});

$scope.events = new SimplePubSub();
$scope.tagList = new TagList($scope.options, $scope.events);

this.registerAutocomplete = function() {
Expand Down Expand Up @@ -172,7 +173,15 @@ tagsInput.directive('tagsInput', function($timeout, $document, tagsInputConfig)
tagList = scope.tagList,
events = scope.events,
options = scope.options,
input = element.find('input');
input = element.find('input'),
validationOptions = ['minTags', 'maxTags', 'allowLeftoverText'],
setElementValidity;

setElementValidity = function() {
ngModelCtrl.$setValidity('maxTags', angular.isUndefined(options.maxTags) || scope.tags.length <= options.maxTags);
ngModelCtrl.$setValidity('minTags', angular.isUndefined(options.minTags) || scope.tags.length >= options.minTags);
ngModelCtrl.$setValidity('leftoverText', options.allowLeftoverText ? true : !scope.newTag.text);
};

events
.on('tag-added', scope.onTagAdded)
Expand All @@ -199,7 +208,12 @@ tagsInput.directive('tagsInput', function($timeout, $document, tagsInputConfig)
tagList.addText(scope.newTag.text);
}

ngModelCtrl.$setValidity('leftoverText', options.allowLeftoverText ? true : !scope.newTag.text);
setElementValidity();
}
})
.on('option-change', function(e) {
if (validationOptions.indexOf(e.name) !== -1) {
setElementValidity();
}
});

Expand All @@ -222,9 +236,8 @@ tagsInput.directive('tagsInput', function($timeout, $document, tagsInputConfig)
tagList.items = scope.tags;
});

scope.$watch('tags.length', function(value) {
ngModelCtrl.$setValidity('maxTags', angular.isUndefined(options.maxTags) || value <= options.maxTags);
ngModelCtrl.$setValidity('minTags', angular.isUndefined(options.minTags) || value >= options.minTags);
scope.$watch('tags.length', function() {
setElementValidity();
});

input
Expand Down
25 changes: 25 additions & 0 deletions test/configuration.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ describe('configuration service', function() {

attrs = {};
$scope.options = {};
$scope.events = { trigger: angular.noop };
});

it('loads literal values from attributes', function() {
Expand Down Expand Up @@ -106,6 +107,30 @@ describe('configuration service', function() {
});
});

it('triggers an event when an interpolated value change', function() {
// Arrange
provider.setActiveInterpolation('foo', { prop1: true });
$scope.$parent.prop1 = 'foobar';
attrs.prop1 = '{{ prop1 }}';

$scope.events = jasmine.createSpyObj('events', ['trigger']);

var callback;
attrs.$observe = jasmine.createSpy().and.callFake(function(name, cb) {
callback = cb;
});

// Act
service.load('foo', $scope, attrs, {
prop1: [String]
});

callback('barfoo');

// Assert
expect($scope.events.trigger).toHaveBeenCalledWith('option-change', { name: 'prop1', newValue: 'barfoo' });
});

it('loads default values when attributes are missing', function() {
// Act
service.load('foo', $scope, attrs, {
Expand Down
50 changes: 50 additions & 0 deletions test/tags-input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,23 @@ describe('tags-input directive', function() {
expect($scope.form.tags.$valid).toBe(true);
expect($scope.form.tags.$error.minTags).toBe(false);
});

it('re-validates the element when the min-tags option changes', function() {
// Arrange
compileWithForm('min-tags="2"', 'name="tags"');

$scope.tags = generateTags(2);
$scope.$digest();

// Act
isolateScope.options.minTags = 3;
isolateScope.events.trigger('option-change', { name: 'minTags' });
$scope.$digest();

// Assert
expect($scope.form.tags.$invalid).toBe(true);
expect($scope.form.tags.$error.minTags).toBe(true);
});
});


Expand Down Expand Up @@ -883,6 +900,23 @@ describe('tags-input directive', function() {
expect($scope.form.tags.$valid).toBe(true);
expect($scope.form.tags.$error.maxTags).toBe(false);
});

it('re-validates the element when the max-tags option changes', function() {
// Arrange
compileWithForm('max-tags="2"', 'name="tags"');

$scope.tags = generateTags(2);
$scope.$digest();

// Act
isolateScope.options.maxTags = 1;
isolateScope.events.trigger('option-change', { name: 'maxTags' });
$scope.$digest();

// Assert
expect($scope.form.tags.$invalid).toBe(true);
expect($scope.form.tags.$error.maxTags).toBe(true);
});
});

describe('display-property option', function() {
Expand Down Expand Up @@ -997,6 +1031,22 @@ describe('tags-input directive', function() {
expect($scope.form.tags.$valid).toBe(true);
expect($scope.form.tags.$error.leftoverText).toBe(false);
});

it('re-validates the element when the allow-leftover-text option changes', function() {
// Arrange
compileWithForm('allow-leftover-text="true"', 'name="tags"');
newTag('foo');
newTag('Foo');
isolateScope.events.trigger('input-blur');

// Act
isolateScope.options.allowLeftoverText = false;
isolateScope.events.trigger('option-change', { name: 'allowLeftoverText' });

// Assert
expect($scope.form.tags.$invalid).toBe(true);
expect($scope.form.tags.$error.leftoverText).toBe(true);
});
});

describe('add-from-autocomplete-only option', function() {
Expand Down
21 changes: 16 additions & 5 deletions test/test-page.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@
placeholder="{{placeholder}}"
remove-tag-symbol="{{removeTagSymbol}}"
max-length="5"
add-from-autocomplete-only="false">
add-from-autocomplete-only="false"
min-tags="{{minTags}}"
max-tags="{{maxTags}}"
allow-leftover-text="{{allowLeftoverText}}">
<auto-complete source="loadItems2($query)"
debounce-delay="0"
min-length="1"
Expand All @@ -62,9 +65,14 @@
<p>Errors: {{form.tags.$error}}</p>
</form>

<input type="text" ng-model="placeholder"/><span>{{placeholder}}</span>
<input type="text" ng-model="removeTagSymbol"/>
<input type="checkbox" ng-model="addOnEnter"/><span>{{addOnEnter}}</span>
<p><label>placeholder:<input type="text" ng-model="placeholder"/>{{placeholder}}</label></p>
<p><label>removeTagSymbol:<input type="text" ng-model="removeTagSymbol"/></label></p>
<p><label>addOnEnter:<input type="checkbox" ng-model="addOnEnter"/>{{addOnEnter}}</label></p>
<p><label>minTags:<input type="text" ng-model="minTags"/></label>{{minTags}}</p>
<p><label>maxTags:<input type="text" ng-model="maxTags"/></label>{{maxTags}}</p>
<p><label>allowLeftoverText:<input type="checkbox" ng-model="allowLeftoverText"/></label></p>

<input type="text" ng-model="removeTagSymbol"/>

<script type="text/javascript">
angular.module('app', ['ngTagsInput'])
Expand Down Expand Up @@ -129,7 +137,10 @@
.setActiveInterpolation('tagsInput', {
placeholder: true,
removeTagSymbol: true,
addOnEnter: true
addOnEnter: true,
allowLeftoverText: true,
maxTags: true,
minTags: true
});
});
</script>
Expand Down

0 comments on commit e89f268

Please sign in to comment.