Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(ngList) Fix model to view rendering when using a custom separator #2561

Closed
wants to merge 5 commits into from
Closed
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
20 changes: 17 additions & 3 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,8 @@ var requiredDirective = function() {
* @element input
* @param {string=} ngList optional delimiter that should be used to split the value. If
* specified in form `/something/` then the value will be converted into a regular expression.
* @param {string=} ngListJoin optional string to use when joining elements of the array.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be just trim of the ngList. You can not have different join and separator values.

* This is especially useful when the delimeter is a regular expression. The default value is ', '.
*
* @example
<doc:example>
Expand Down Expand Up @@ -1263,8 +1265,20 @@ var ngListDirective = function() {
return {
require: 'ngModel',
link: function(scope, element, attr, ctrl) {
var match = /\/(.*)\//.exec(attr.ngList),
separator = match && new RegExp(match[1]) || attr.ngList || ',';
// Get the attribute values directly from the element rather than the
// attr map otherwise the attribute values will be trimmed of whitespace
var separatorAttribute = element[0].getAttribute(attr.$attr['ngList'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be: attr.ngList not attr.$attr.ngList

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the discussion on whitespace above for why I use getAttribute here.

var joinAttribute = element[0].getAttribute(attr.$attr['ngListJoin'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

var separator, joinedby;
var match = /\/(.*)\//.exec(separatorAttribute);
if (match) {
// This is a regex pattern - always use ', ' to join elements when ngListJoin is undefined
separator = new RegExp(match[1])
joinedby = joinAttribute || ', ';
}else{
separator = separatorAttribute || ','
joinedby = joinAttribute || separatorAttribute || ', ';
}

var parse = function(viewValue) {
var list = [];
Expand All @@ -1281,7 +1295,7 @@ var ngListDirective = function() {
ctrl.$parsers.push(parse);
ctrl.$formatters.push(function(value) {
if (isArray(value)) {
return value.join(', ');
return value.join(joinedby);
}

return undefined;
Expand Down
75 changes: 73 additions & 2 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1009,9 +1009,62 @@ describe('input', function() {
it('should allow custom separator', function() {
compileInput('<input type="text" ng-model="list" ng-list=":" />');

changeInputValueTo('a,a');
expect(scope.list).toEqual(['a,a']);
// model -> view
scope.$apply(function() {
scope.list = ['x', 'y', 'z'];
});
expect(inputElm.val()).toBe('x:y:z'); //Should use the separator as the join string

// view -> model
changeInputValueTo('a:b');
expect(scope.list).toEqual(['a', 'b']);
});


it('should allow custom separator with whitespace', function() {
compileInput('<input type="text" ng-model="list" ng-list=" or " />');

changeInputValueTo('House or Door or Chair');
expect(scope.list).toEqual(['House', 'Door', 'Chair']);
});


it('should allow custom join string', function() {
compileInput('<input type="text" ng-model="list" ng-list ng-list-join=" and "/>');

// model -> view
scope.$apply(function() {
scope.list = ['x', 'y', 'z'];
});
expect(inputElm.val()).toBe('x and y and z');
});


it('should allow custom separator and custom join string', function() {
compileInput('<input type="text" ng-model="list" ng-list=":" ng-list-join=" and "/>');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes no sense. How can you split on ':' and join on " and ". This means that during edit the text would change on you if someone changes the model. This is undesirable. I really think that there should be on ng-list and and some intelligent logic about how to ignore whitespace. So if it is work white space is added, but otherwise it is ignored or something like that.


// model -> view
scope.$apply(function() {
scope.list = ['x', 'y', 'z'];
});
expect(inputElm.val()).toBe('x and y and z');

// view -> model
changeInputValueTo('a:b');
expect(scope.list).toEqual(['a', 'b']);
});


it('should allow custom separator and custom join string in alternative attribute form', function() {
compileInput('<input type="text" ng-model="list" ng:list=":" data-ng-list-join=" and "/>');

// model -> view
scope.$apply(function() {
scope.list = ['x', 'y', 'z'];
});
expect(inputElm.val()).toBe('x and y and z');

// view -> model
changeInputValueTo('a:b');
expect(scope.list).toEqual(['a', 'b']);
});
Expand All @@ -1020,12 +1073,30 @@ describe('input', function() {
it('should allow regexp as a separator', function() {
compileInput('<input type="text" ng-model="list" ng-list="/:|,/" />');

// model -> view
scope.$apply(function() {
scope.list = ['x', 'y', 'z'];
});
expect(inputElm.val()).toBe('x, y, z'); //Should use ', ' as the default join string

// view -> model
changeInputValueTo('a,b');
expect(scope.list).toEqual(['a', 'b']);

changeInputValueTo('a,b: c');
expect(scope.list).toEqual(['a', 'b', 'c']);
});


it('should allow regexp as a separator and custom join string', function() {
compileInput('<input type="text" ng-model="list" ng-list="/:|,/" ng-list-join="; " />');

// model -> view
scope.$apply(function() {
scope.list = ['x', 'y', 'z'];
});
expect(inputElm.val()).toBe('x; y; z');
});
});

describe('required', function() {
Expand Down