Skip to content

Commit 091228f

Browse files
author
Gonzalo Ruiz de Villa
committed
fix(input): ngList is updated when array model values are changed
fixes angular#1751 When the model referenced the same same array and the array values where changed, the list wasn't updated. Now watchCollection is used to detect changes in NgModelController. Changes in input.js breaked tests: — select select-multiple should require — select ngOptions ngRequired should treat an empty array as invalid when `multiple` attribute used Changes in select.js fixed them again: changes in the collections should trigger the formatters and render again. BEHAVIOUR CHANGE There is a change in the behaviour of ngList when typing a list. When “a , b” is typed is automatically changed to “a, b”.
1 parent 767ee85 commit 091228f

File tree

3 files changed

+48
-10
lines changed

3 files changed

+48
-10
lines changed

src/ng/directive/input.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,11 +1097,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
10971097
// model -> value
10981098
var ctrl = this;
10991099

1100-
$scope.$watch(function ngModelWatch() {
1100+
$scope.$watchCollection($attr.ngModel, function ngModelWatch(newValue, oldValue) {
11011101
var value = ngModelGet($scope);
11021102

11031103
// if scope model value and ngModel value are out of sync
1104-
if (ctrl.$modelValue !== value) {
1104+
if (!equals(ctrl.$modelValue, oldValue)) {
11051105

11061106
var formatters = ctrl.$formatters,
11071107
idx = formatters.length;

src/ng/directive/select.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -262,21 +262,24 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
262262
}
263263

264264
function setupAsMultiple(scope, selectElement, ctrl) {
265-
var lastView;
266265
ctrl.$render = function() {
267266
var items = new HashMap(ctrl.$viewValue);
268267
forEach(selectElement.find('option'), function(option) {
269268
option.selected = isDefined(items.get(option.value));
270269
});
271270
};
272271

273-
// we have to do it on each watch since ngModel watches reference, but
274-
// we need to work of an array, so we need to see if anything was inserted/removed
275-
scope.$watch(function selectMultipleWatch() {
276-
if (!equals(lastView, ctrl.$viewValue)) {
277-
lastView = copy(ctrl.$viewValue);
278-
ctrl.$render();
272+
scope.$watchCollection(attr.ngModel, function selectMultipleWatch(newValue, oldValue) {
273+
var formatters = ctrl.$formatters,
274+
idx = formatters.length,
275+
value = newValue;
276+
277+
ctrl.$modelValue = value;
278+
while(idx--) {
279+
value = formatters[idx](value);
279280
}
281+
ctrl.$viewValue = value;
282+
ctrl.$render();
280283
});
281284

282285
selectElement.on('change', function() {
@@ -395,6 +398,19 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
395398
// TODO(vojta): can't we optimize this ?
396399
scope.$watch(render);
397400

401+
scope.$watchCollection(attr.ngModel, function selectMultipleWatch(newValue, oldValue) {
402+
var formatters = ctrl.$formatters,
403+
idx = formatters.length,
404+
value = newValue;
405+
406+
ctrl.$modelValue = value;
407+
while(idx--) {
408+
value = formatters[idx](value);
409+
}
410+
ctrl.$viewValue = value;
411+
render();
412+
});
413+
398414
function render() {
399415
// Temporary location for the option groups before we render them
400416
var optionGroups = {'':[]},

test/ng/directive/inputSpec.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ describe('input', function() {
11811181
expect(scope.list).toEqual(['a']);
11821182

11831183
changeInputValueTo('a , b');
1184-
expect(inputElm.val()).toEqual('a , b');
1184+
expect(inputElm.val()).toEqual('a, b');
11851185
expect(scope.list).toEqual(['a', 'b']);
11861186
});
11871187

@@ -1224,6 +1224,28 @@ describe('input', function() {
12241224
changeInputValueTo('a,b: c');
12251225
expect(scope.list).toEqual(['a', 'b', 'c']);
12261226
});
1227+
1228+
it("should detect changes in the values of an array", function () {
1229+
var list = ['x', 'y', 'z'];
1230+
compileInput('<input type="text" ng-model="list" ng-list />');
1231+
scope.$apply(function() {
1232+
scope.list = list;
1233+
});
1234+
expect(inputElm.val()).toBe('x, y, z');
1235+
scope.$apply(function() {
1236+
list.unshift('w');
1237+
});
1238+
expect(inputElm.val()).toBe('w, x, y, z');
1239+
});
1240+
1241+
it('should be invalid if empty', function() {
1242+
compileInput('<input name="namesInput" ng-model="list" ng-list required/>');
1243+
changeInputValueTo('a');
1244+
expect(inputElm).toBeValid();
1245+
changeInputValueTo('');
1246+
expect(inputElm).toBeInvalid();
1247+
});
1248+
12271249
});
12281250

12291251
describe('required', function() {

0 commit comments

Comments
 (0)