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

Commit 97e2d00

Browse files
cstephejelbourn
authored andcommitted
fix(select(multiple)): Remove side-effects to forms when adding md-select elements (#11491)
* Remove code which causes side effect on form elements * Add a delay to the initial render of a multiple select so that it doesn't set the element to dirty on initialization. Fixes #11490
1 parent 71e0411 commit 97e2d00

File tree

2 files changed

+50
-14
lines changed

2 files changed

+50
-14
lines changed

src/components/select/select.js

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -291,15 +291,6 @@ function SelectDirective($mdSelect, $mdUtil, $mdConstant, $mdTheming, $mdAria, $
291291
findSelectContainer();
292292
$mdTheming(element);
293293

294-
if (formCtrl && angular.isDefined(attr.multiple)) {
295-
$mdUtil.nextTick(function() {
296-
var hasModelValue = ngModelCtrl.$modelValue || ngModelCtrl.$viewValue;
297-
if (hasModelValue) {
298-
formCtrl.$setPristine();
299-
}
300-
});
301-
}
302-
303294
var originalRender = ngModelCtrl.$render;
304295
ngModelCtrl.$render = function() {
305296
originalRender();
@@ -673,13 +664,26 @@ function SelectMenuDirective($parse, $mdUtil, $mdConstant, $mdTheming) {
673664
if (deregisterCollectionWatch) deregisterCollectionWatch();
674665

675666
if (self.isMultiple) {
667+
// We want to delay the render method so that the directive has a chance to load before
668+
// rendering, this prevents the control being marked as dirty onload.
669+
var loaded = false;
670+
var delayedRender = function(val) {
671+
if (!loaded) {
672+
$mdUtil.nextTick(function () {
673+
renderMultiple(val);
674+
loaded = true;
675+
});
676+
} else {
677+
renderMultiple(val);
678+
}
679+
};
676680
ngModel.$validators['md-multiple'] = validateArray;
677-
ngModel.$render = renderMultiple;
681+
ngModel.$render = delayedRender;
678682

679683
// watchCollection on the model because by default ngModel only watches the model's
680-
// reference. This allowed the developer to also push and pop from their array.
684+
// reference. This allows the developer to also push and pop from their array.
681685
$scope.$watchCollection(self.modelBinding, function(value) {
682-
if (validateArray(value)) renderMultiple(value);
686+
if (validateArray(value)) delayedRender(value);
683687
});
684688

685689
ngModel.$isEmpty = function(value) {

src/components/select/select.spec.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ describe('<md-select>', function() {
507507

508508
$rootScope.$digest();
509509
$rootScope.$digest();
510-
510+
$timeout.flush();
511511
expect(label.text()).toBe('One, Three');
512512
expect(label.hasClass('md-select-placeholder')).toBe(false);
513513

@@ -1087,6 +1087,36 @@ describe('<md-select>', function() {
10871087
expect($rootScope.testForm.$pristine).toBe(true);
10881088
});
10891089

1090+
it('should not change a dirty form to pristine', function() {
1091+
$rootScope.rows = [[2], [4,3]];
1092+
$rootScope.filterTerm;
1093+
$rootScope.opts = [1, 2, 3, 4];
1094+
var select = $compile('<form name="testForm">' +
1095+
'<div ng-repeat="item in rows | filter:filterTerm">' +
1096+
'<md-select ng-model="item" name="multiSelect" multiple id="{{$index}}">' +
1097+
'<md-option ng-repeat="opt in opts" ng-value="opt"></md-option>' +
1098+
'</md-select>' +
1099+
'</div></form>')($rootScope);
1100+
$rootScope.$apply();
1101+
$timeout.flush();
1102+
expect(select.find('md-select').length).toBe(2);
1103+
$rootScope.testForm.$setDirty();
1104+
$rootScope.$apply();
1105+
$timeout.flush();
1106+
expect($rootScope.testForm.$pristine).toBeFalsy();
1107+
$rootScope.filterTerm = "2";
1108+
$rootScope.$apply();
1109+
$timeout.flush();
1110+
expect(select.find('md-select').length).toBe(1);
1111+
expect($rootScope.testForm.$pristine).toBeFalsy();
1112+
$rootScope.filterTerm = "";
1113+
$rootScope.$apply();
1114+
$timeout.flush();
1115+
expect(select.find('md-select').length).toBe(2);
1116+
expect($rootScope.testForm.$dirty).toBeTruthy();
1117+
expect($rootScope.testForm.$pristine).toBeFalsy();
1118+
});
1119+
10901120
it('should correctly update the input containers label', function() {
10911121
var el = setupSelect('ng-required="isRequired" ng-model="someModel"');
10921122
var label = el.find('label');
@@ -1466,7 +1496,9 @@ describe('<md-select>', function() {
14661496

14671497
function setupSelectMultiple(attrs, options, skipLabel, scope) {
14681498
attrs = (attrs || '') + ' multiple';
1469-
return setupSelect(attrs, options, skipLabel, scope);
1499+
var toReturn = setupSelect(attrs, options, skipLabel, scope);
1500+
$timeout.flush();
1501+
return toReturn;
14701502
}
14711503

14721504
function optTemplate(options, compileOpts) {

0 commit comments

Comments
 (0)