Fix Angular datepicker to keep up with $digest cycle
authorColeman Watts <coleman@civicrm.org>
Thu, 28 Jul 2022 21:01:07 +0000 (17:01 -0400)
committerColeman Watts <coleman@civicrm.org>
Fri, 29 Jul 2022 14:44:26 +0000 (10:44 -0400)
ang/crmUi.js
tests/karma/unit/crmMailingRadioDateSpec.js

index dde019030d01601f13d2d7e88b80b239860a7f79..602e7048457122e65d16e21689206e9138e7968e 100644 (file)
@@ -67,7 +67,7 @@
     // Simple wrapper around $.crmDatepicker.
     // example with no time input: <input crm-ui-datepicker="{time: false}" ng-model="myobj.datefield"/>
     // example with custom date format: <input crm-ui-datepicker="{date: 'm/d/y'}" ng-model="myobj.datefield"/>
-    .directive('crmUiDatepicker', function () {
+    .directive('crmUiDatepicker', function ($timeout) {
       return {
         restrict: 'AE',
         require: 'ngModel',
           element
             .crmDatepicker(scope.crmUiDatepicker)
             .on('change', function() {
-              var requiredLength = 19;
-              if (scope.crmUiDatepicker && scope.crmUiDatepicker.time === false) {
-                requiredLength = 10;
-              }
-              if (scope.crmUiDatepicker && scope.crmUiDatepicker.date === false) {
-                requiredLength = 8;
-              }
-              ngModel.$setValidity('incompleteDateTime', !($(this).val().length && $(this).val().length !== requiredLength));
+              // Because change gets triggered from the $render function we could be either inside or outside the $digest cycle
+              $timeout(function() {
+                var requiredLength = 19;
+                if (scope.crmUiDatepicker && scope.crmUiDatepicker.time === false) {
+                  requiredLength = 10;
+                }
+                if (scope.crmUiDatepicker && scope.crmUiDatepicker.date === false) {
+                  requiredLength = 8;
+                }
+                ngModel.$setValidity('incompleteDateTime', !(element.val().length && element.val().length !== requiredLength));
+              });
             });
         }
       };
index 0e47406b3e78563575ab85e364b36c61eb7d408c..97074bcaa024f3777283198252d097f04e652455 100644 (file)
@@ -55,6 +55,7 @@ describe('crmMailingRadioDate', function() {
 
       model.the_date = ' ';
       $rootScope.$digest();
+      $timeout.flush();
       expect($rootScope.myForm.$valid).toBe(false);
       expect(element.find('.radio-now').prop('checked')).toBe(false);
       expect(element.find('.radio-at').prop('checked')).toBe(true);
@@ -63,6 +64,7 @@ describe('crmMailingRadioDate', function() {
 
       model.the_date = '2014-01-01';
       $rootScope.$digest();
+      $timeout.flush();
       expect($rootScope.myForm.$valid).toBe(false);
       expect(element.find('.radio-now').prop('checked')).toBe(false);
       expect(element.find('.radio-at').prop('checked')).toBe(true);
@@ -72,6 +74,7 @@ describe('crmMailingRadioDate', function() {
 
       model.the_date = '02:03:00';
       $rootScope.$digest();
+      $timeout.flush();
       expect($rootScope.myForm.$valid).toBe(false);
       expect(element.find('.radio-now').prop('checked')).toBe(false);
       expect(element.find('.radio-at').prop('checked')).toBe(true);
@@ -113,6 +116,7 @@ describe('crmMailingRadioDate', function() {
       var ndate = new Date(year, month-1, day, 0, 0, 0);
       model.the_date = currentDate;
 
+      $timeout.flush();
       $rootScope.$digest();
       expect($rootScope.myForm.$valid).toBe(true);
       expect(element.find('.radio-now').prop('checked')).toBe(false);
@@ -132,6 +136,7 @@ describe('crmMailingRadioDate', function() {
 
       element.find('.radio-now').click().trigger('click').trigger('change');
       element.find('.crm-form-date').datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', '2014-01-03')).trigger('change');
+      $timeout.flush();
       $rootScope.$digest();
       expect(model.the_date).toBe('2014-01-03');
       expect($rootScope.myForm.$valid).toBe(false);
@@ -146,6 +151,7 @@ describe('crmMailingRadioDate', function() {
       expect(element.find('.radio-at').prop('checked')).toBe(true);
 
       element.find('.crm-form-date').datepicker('setDate', '').trigger('change');
+      $timeout.flush();
       $rootScope.$digest();
       expect(model.the_date).toBe('04:05:00');
       expect($rootScope.myForm.$valid).toBe(false);
@@ -153,6 +159,7 @@ describe('crmMailingRadioDate', function() {
       expect(element.find('.radio-at').prop('checked')).toBe(true);
 
       element.find('.radio-now').click().trigger('click').trigger('change');
+      $timeout.flush();
       $rootScope.$digest();
       expect(model.the_date).toBe(null);
       expect($rootScope.myForm.$valid).toBe(true);