UPDATE 1: the question will be enhanced based on the feedback from the comments.
UPDATE 2: Some progress has been made. Need additinoal help to get through. Please read below.
UPDATE 3: Bug fixing in provided sample code causing duplication in table rows when compiling the element using $compile(el[0])(scope);
.
On page load, a list of field names is retrieved from Database which indicates what fields are required using ajax call getRequiredFieldInfo()
. This call must be completed successfully before executing the related angular code under the directive check-if-required
to manipulate the required
attribute. This directive must loop over all input fields and mark them as required based on the list which is retrieved from the Database.
I did some research and found this post which seems to be the closest to my requirements:
https://stackoverflow.com/a/28207652/4180447
and finally found a working jsfiddle version here (updated):
http://jsfiddle.net/tarekahf/d50tr99u/
I can use the following simple approach:
<input name="firstName" type="text" foo ng-required="isFieldRequired('firstName')" />
The function isFieldRequired()
will check if the passed field name is found in the list, it will return true.
The problem with this approach is that I have to add this function to each and every field which might be required.
Also, will have to pass the field name each time. To be more efficient, I will have to use a directive on the parent element div
or fieldset
which will allow me to access all child elements, and process the required attributes for the all the input elements.
This directive need to be changed as follows:
To be added to the parent element of the group of fields whose
required
attribute will be processed and modified if needed.Compare the element name against the list of fields to set as required and apply the change accordingly.
The updated code (as I am researching the solution):
STYLE
input.ng-invalid, li.ng-invalid {
background:#F84072;
border: 2px red solid;
}
HTML - NAVIGATION TABS:
<ul class="nav nav-pills">
<li ng-class="{'ng-invalid':mainForm.homeForm.$invalid && mainPromiseResolved}" class="active"><a data-toggle="pill" href="#home"><%=homeTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.clientForm.$invalid && mainPromiseResolved}"><a data-toggle="pill" href="#menu1"><%=clientTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.appraiserForm.$invalid && mainPromiseResolved}"> <a data-toggle="pill" href="#menu2"><%=appraiserTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.propertyForm.$invalid && mainPromiseResolved}"><a data-toggle="pill" href="#menu3"><%=propertyTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.serviceForm.$invalid && mainPromiseResolved}"><a data-toggle="pill" href="#menu4"><%=servicesTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.constructionStage.$invalid && mainPromiseResolved}"><a data-toggle="pill" href="#menu5"><%=constructionStageTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.costForm.$invalid && mainPromiseResolved}"><a data-toggle="pill" href="#menu6"><%=costTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.certificationForm.$invalid && mainPromiseResolved}" ng-click="redrawCanvas()"><a data-toggle="pill" href="#menu7"><%=certificationTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.photosForm.$invalid && mainPromiseResolved}"><a data-toggle="pill" href="#menu8"><%=photoTabName%></a></li>
<li ng-class="{'ng-invalid':mainForm.mapForm.$invalid && mainPromiseResolved}"><a data-toggle="pill" href="#menu9"><%=locationTabName%></a></li>
</ul>
HTML - Form
<div id="menu2" class="tab-pane fade" ng-form="appraiserForm">
<fieldset ng-disabled="isAppraiserSigned()" check-if-required>
<input type="text" id="appraiser_name" name="appraiser_name" ng-model="sigRoles.appraiser.roleNameModel" style="width: 536px; ">
<input type="text" id="appraiser_company" style="width: 536px; ">
...
...
</fieldset>
</div>
Javascrip:
app.controller('formMainController', ['$scope', '$timeout', '$q', function($scope, $timeout, $q) {
$scope.runProcessAndInit = function () {
var q = $q.defer(); //Create a promise controller
angular.element(document).ready(function(){
//perform all client updates here
q.resolve('success'); //notify execution is completed successfully - inside document 'ready' event.
})
return q.promise; //return the promise object.
}
//mainPromiseResolved is used to indicate all ajax calls and client updates are done.
$scope.mainPromiseResolved = false;
$scope.mainPromise = $scope.runProcessAndInit();
$scope.mainPromise.then(function(success) {
//debugger;
$scope.$broadcast('event:force-model-update');
//mainPromiseResolved is mainly used in angular validation to prevent showing errors until all client updates are done.
$scope.mainPromiseResolved = true;
return 'main promise done';
})
$scope.isFieldRequired = function (prmFieldName) {
var isFound = false;
var oRequiredField = formView.getRequiredField();
findField: {
for(var subformName in oRequiredField) {
isFound = prmFieldName in oRequiredField[subformName];
if (isFound) {
break findField;
}
}
}
return isFound;
}
function getRequiredFieldInfo() {
var q = $q.defer();
var appUrl = getAppURL();
$.get(appUrl + "/servlet/..."
+ "×tamp=" + new Date().getTime(),
function(data, status){
//console.log("json fields:" + data);
var obj = JSON.parse(data);
formView.setRequiredField(obj);
q.resolve('success');
// console.log(JSON.stringify(formView.getRequiredField()));
});
return q.promise;
}
$scope.requiredFieldsPromise = getRequiredFieldInfo();
}]);
app.directive('checkIfRequired', ['$compile', function ($compile) {
return {
require: '?ngModel',
link: function (scope, el, attrs, ngModel) {
if (!ngModel) {
//return;
}
//debugger;
var children = $(":input", el);
angular.element(document).ready(function (){
scope.requiredFieldsPromise.then(function(success) {
//remove the attribute to avoid recursive calls
el.removeAttr('check-if-required');
//Comment line below as it caused duplication in table raws, and I don't know why.
//$compile(el[0])(scope);
angular.forEach(children, function(value, key) {
//debugger;
if (scope.isFieldRequired(value.id)) {
angular.element(value).attr('required', true);
//el.removeAttr('check-if-required');
$compile(value)(scope);
}
});
})
})
}
};
}]);
I've already made some progress. However, I still need more help. Following is the status:
- Done: get required fields list from DB and then execute code in directive to manipulate the
required
attribute. Done: Loop over the child input elements from a given angular element
el
which is passed to link functionfunction (scope, el, attrs, ngModel)
.Done: Add
required
attribute to each child element ifisFieldRequired(fieldName)
is true?Done: Use promise to ensure all ajax DB calls and client updates are done before executing angular code.
How to recursively loop over the child elements if they are nested inside another
ng-form
subform ordiv
element?How to ensure that each element has ngModel object?
How to restrict the directive to
div
,fieldsset
or similar elements?
Tarek