UPDATE 1: add more details.
UPDATE 2: added the plunker code to reproduce the problem. See link below.
UPDATE 3: I got reply from angular team on github. Check it here.
UPDATE 4: Posted updated on github as requested by AngularJS team. Also, the proposed solution I added before turned out that it is creating a new visual problem. See details below.
UPDATE 5: Get another feedback from Angular Team. I think we are 80% close to finding a solution.
I implemented Angular UI datepicker in my project, and after a while, I noticed that when I open the popup box, and click on the month to change to another month, another popup window is displayed on top of the existing popup. See snapshot below to get more details:
Originally, when I click on the month, the current popup should disappear, and another popup should show to select the new month.
However, when I click twice on the month (highlighted in yellow below), the popup will disappear, and it will work fine. But, after I select a date, then the problem will come back.
I am using the same sample code from angular ui datepicker official demo website. Find below the related website and plunker code:
http://angular-ui.github.io/bootstrap/versioned-docs/1.3.3/#/datepickerPopup http://plnkr.co/edit/lEgJ9eC9SzBWsgVhhQkq?p=preview
My code is exactly same as the code in the plunker sample above. The only difference is that I am using $compile
service to add required field validation dynamically.
After extensive troubleshooting, and found out that $compile()
service causes this behaviour. I did some research, and found out that the $compile
service also causes duplicate items in drop-down list or the select
element. I used the proposed workaround and it worked. See code below:
$compile(child)(elmScope, function (clone) {
angular.element(child).after(clone);
angular.element(child).remove();
});
The reason why I am using $compile
is to add dynamic validation rules from DB to the elements using this approach here.
After I got reply from angular team on github, I found that they suggested this fix:
.directive('checkIfRequired', ['$compile', function ($compile) {
return {
priority: 1000,
terminal: true,
/*require: '?ngModel',*/
//JIRA: NE-2535 - inject the current 'ngForm' controller with this directive
// This is required to add automatice error message when the Form Field is invalid.
require: '?^form',
link: function (scope, el, attrs, ngForm) {
el.removeAttr('check-if-required');
el.attr('ng-required', 'true');
$compile(el, 1000)(scope);
}
};
}]);
When I tried to apply the suggested fix, I am getting large number of errors. It seems that when using terminal=true
then all the code in the inner elements under 'ng-init' was not executed. I noticed that many scope variables become "undefined".
This is the final update on github in attempt to find a solution. See code below:
app.directive('checkIfRequired', ['$compile', '$timeout', function ($compile, $timeout) {
return {
priority: 2000,
terminal: true,
/*link: function (scope, el, attrs) {
el.removeAttr('check-if-required');
var children = $(':input', el);
children.each(function(key, child) {
if (child && child.id === 'test_me') {
angular.element(child).attr('ng-required', 'true');
}
});
$compile(children)(scope);
},*/
compile: function (el, attrs) {
el.removeAttr('check-if-required');
var children = $(':input', el);
children.each(function(key, child) {
if (child && child.id === 'test_me') {
angular.element(child).attr('ng-required', 'true');
}
});
var compiled = $compile(children, null, 2000);
return function( scope ) {
compiled( scope );
};
}
};
}]);
Appreciate your help to apply the fix in the right way in my project. See the related parts of the code here.
I posted a solution before, but then I removed it. I noticed later on that it has caused a funny stacking effect which is worse than that I reported earlier.
Here is the update on github with full details:
https://github.com/angular/angular.js/issues/15956#issuecomment-300324812
Will keep updating when I get reply from AngularJS team.
Please bear with me until I find a permanent solution.
In order the avoid this problem must not use $compile
service, or if you must use this code sample to fix the problem:
$compile(child)(elmScope, function (clone) {
angular.element(child).after(clone);
angular.element(child).remove();
});
Hope this will be of value to others who are facing the same problem.
This is to perform correction. The solution added before didn't work as expected.
For complete details, check update on github:
https://github.com/angular/angular.js/issues/15956#issuecomment-300324812
For now I can propose a different approach to use dynamic validation rules which are stored on DB and to be loaded with AngularJS form.
Refer to the code parts here as a basis for this proposed solution:
Angular dynamic required validation of group of fields with field highlight if invalid
Instead of using $compile
, in the directive check-if-required
, create a new scope
variable for the field child.id
as such:
if (scope.isFieldRequired(child.id)) {
//angular.element(child).attr('ng-required', "true");
//$compile(child)(elmScope);
scope.RootController.ngRequired[child.id] = true;
} else {
scope.RootController.ngRequired[child.id] = false;
}
When you defined the element, ensure it similar to the following:
<input id="customer_id" name="customer_id" ng-model="customer_id" ng-required="RootController.ngRequired.customer_id"/>
Basically, all validation rules must be linked to a scope variable which must be defined in the root Angular Controller RootController'. For simplicity, the required validation will be under
RootController.ngRequired`.
It is necessary to define RootController
using the controller as
notation at the top most outer controller. Otherwise, the above solution won't work. Following is a sample element definition for the root controller:
<body ng-app="myApp" ng-controller="formMainController as RootController">
...
</body>
If I don't get satisfactory reply from Angular team regarding the issues found due to using $compile
, I will then switch to the above method. I am almost certain that the above method will work.
If you have any feedback, please do so.
Tarek