← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 17959: event capture - live input validation

 

------------------------------------------------------------
revno: 17959
committer: Abyot Asalefew Gizaw <abyota@xxxxxxxxx>
branch nick: dhis2
timestamp: Tue 2015-01-13 18:31:37 +0100
message:
  event capture - live input validation
modified:
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/i18n/i18n_app.properties
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/index.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/controllers.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/views/defaultForm.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/views/ec-custom-form.html
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/angular/plugins/dhis2/directives.js
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/angular/plugins/dhis2/services.js


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/i18n/i18n_app.properties'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/i18n/i18n_app.properties	2014-12-18 11:50:32 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/i18n/i18n_app.properties	2015-01-13 17:31:37 +0000
@@ -29,7 +29,6 @@
 go_back=Go back
 form_invalid=Form is invalid. Please check for required fields.
 required=Required
-value_must_be=Value must be
 int_required=Value must be a number
 string_required=Value must be a text
 date_required=Value must be a date
@@ -114,11 +113,18 @@
 update_event=Update Event
 missing_translation_file=Missing Translation File
 missing_translation_using_default=No translation file is found for the selected locale. Using default translation (English).
+value_must_be=Value must be
 number=Number
+value_must_be_Number=Value must be number
+value_must_be_posInt=Value must be positive integer
+value_must_be_negInt=Value must be negative integer
+value_must_be_zeroPositiveInt=Value must be zero or positive integer
+value_must_be_int=Value must be integer 
 posInt=Positive Integer
 negInt=Negative Integer
 zeroPostitiveInt=Zero or Positive Integer
 loading_tree=Loading orgunit tree
 loading_metadata=Loading meta-data
+future_date_not_allowed=Future date is not allowed
 
 

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/index.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/index.html	2015-01-12 18:24:10 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/index.html	2015-01-13 17:31:37 +0000
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
-<html ng-app="eventCapture">
-<!--<html manifest="cacheManifest.action" ng-app="eventCapture">-->
+<html manifest="cacheManifest.action" ng-app="eventCapture">
     <head>
         <title>Event Capture</title>
 
@@ -60,7 +59,7 @@
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.storage.memory.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.storage.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.contextmenu.js"></script>
-        <!--<script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.appcache.js"></script>-->
+        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.appcache.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/ouwt/ouwt.js"></script>
         <script type="text/javascript" src="scripts/event-capture.js"></script>
         

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/controllers.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/controllers.js	2015-01-12 18:24:10 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/controllers.js	2015-01-13 17:31:37 +0000
@@ -8,6 +8,7 @@
         function($scope,
                 $modal,
                 $timeout,
+                $translate,
                 storage,
                 Paginator,
                 OptionSetService,
@@ -19,11 +20,12 @@
                 DateUtils,
                 CalendarService,
                 CustomFormService,
+                ErrorMessageService,
                 ModalService,
                 DialogService) {
     //selected org unit
     $scope.selectedOrgUnit = '';
-    $scope.treeLoaded = false;
+    $scope.treeLoaded = false;    
     
     $scope.calendarSetting = CalendarService.getSetting();
     
@@ -148,9 +150,16 @@
                     $scope.eventGridColumns.push({name: $scope.selectedProgramStage.reportDateDescription ? $scope.selectedProgramStage.reportDateDescription : 'incident_date', id: 'event_date', type: 'date', compulsory: false, showFilter: false, show: true});
                     $scope.filterTypes['event_date'] = 'date';
                     $scope.filterText['event_date']= {};
-
+                    
+                    var errorMessages = {};
+                    errorMessages['eventDate'] = $translate('required');
                     angular.forEach($scope.selectedProgramStage.programStageDataElements, function(prStDe){
-                        $scope.prStDes[prStDe.dataElement.id] = prStDe;                    
+                        $scope.prStDes[prStDe.dataElement.id] = prStDe;
+                        
+                        errorMessages[prStDe.dataElement.id] = "";
+                        if(prStDe.compulsory){
+                            errorMessages[prStDe.dataElement.id] = $translate('required');
+                        }
 
                         //$scope.newDhis2Event.dataValues.push({id: prStDe.dataElement.id, value: ''});   
                         $scope.newDhis2Event[prStDe.dataElement.id] = '';
@@ -173,7 +182,9 @@
                         if(prStDe.dataElement.type === 'date' || prStDe.dataElement.type === 'int' ){
                             $scope.filterText[prStDe.dataElement.id]= {};
                         }
-                    });           
+                    });
+                    
+                    ErrorMessageService.setErrorMessages(errorMessages);
 
                     //Load events for the selected program stage and orgunit
                     DHIS2EventFactory.getByStage($scope.selectedOrgUnit.id, $scope.selectedProgramStage.id, $scope.pager ).then(function(data){
@@ -730,13 +741,41 @@
     
     $scope.formIsChanged = function(){        
         var isChanged = false;
-        for (var k in $scope.currentEvent) {
-            if ($scope.currentEvent.hasOwnProperty(k)) {
-                if($scope.currentEvent[k] && $scope.currentEventOrginialValue[k] !== $scope.currentEvent[k]){
-                    isChanged = true;
-                }
+        for(var i=0; i<$scope.selectedProgramStage.programStageDataElements.length && !isChanged; i++){
+            var deId = $scope.selectedProgramStage.programStageDataElements[i].dataElement.id;
+            if($scope.currentEvent[deId] && $scope.currentEventOrginialValue[deId] !== $scope.currentEvent[deId]){
+                isChanged = true;
+            }
+        }        
+        if(!isChanged){
+            if($scope.currentEvent.eventDate !== $scope.currentEventOrginialValue.eventDate){
+                isChanged = true;
             }
         }
+        
         return isChanged;
     };
+    
+    $scope.isFormInvalid = function(){
+        if($scope.outerForm.submitted){
+            return $scope.outerForm.$invalid;
+        }
+        
+        var errorMessages = ErrorMessageService.getErrorMessages();
+        
+        for(var k in errorMessages){
+            if( errorMessages.hasOwnProperty(k) && 
+                errorMessages[k] !== "" && 
+                errorMessages[k] !== $translate('required') &&
+                $scope.currentEvent[k]){
+                return true;
+            }
+        }
+        
+        return false;
+    };
+    
+    $scope.getErrorMessage = function(deId){
+        return ErrorMessageService.get(deId);
+    };
 });

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js	2015-01-08 16:39:10 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js	2015-01-13 17:31:37 +0000
@@ -313,4 +313,21 @@
             return e;
         }        
     };
+})
+
+/* Error messages*/
+.service('ErrorMessageService', function(){
+    this.errorMessages = {};
+    
+    this.setErrorMessages = function(errorMessages){  
+        this.errorMessages = errorMessages;        
+    };
+    
+    this.getErrorMessages = function(){
+        return this.errorMessages;
+    };
+    
+    this.get = function(id){
+        return this.errorMessages[id];
+    };
 });
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/views/defaultForm.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/views/defaultForm.html	2015-01-12 18:24:10 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/views/defaultForm.html	2015-01-13 17:31:37 +0000
@@ -10,7 +10,7 @@
                 </th>         
             </tr>                        
         </thead>
-        <tbody id="list">
+        <tbody>
             <tr ng-if="selectedProgramStage.preGenerateUID || editingEventInFull">
                 <td>
                     {{'form_id' | translate}}
@@ -21,10 +21,10 @@
             </tr>
             <tr>
                 <td>
-                    {{selectedProgramStage.reportDateDescription ? selectedProgramStage.reportDateDescription : 'incident_date'| translate }}
+                    {{selectedProgramStage.reportDateDescription ? selectedProgramStage.reportDateDescription : 'incident_date'| translate }}<span class="required">*</span>
                 </td>
                 <td>
-                    <input type="text"                                                   
+                    <input type="text"                           
                            placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
                            d2-date
                            max-date='0'
@@ -32,8 +32,9 @@
                            ng-disabled="editingEventInFull"    
                            ng-required="true" 
                            name="eventDate"
+                           input-field-id="eventDate"
                            style="width:99%;"/>
-                    <span ng-show="currentEvent.eventDate && outerForm.eventDate.$invalid || outerForm.submitted && outerForm.eventDate.$invalid" class="required">{{'date_required'| translate}}</span>
+                    <span ng-show="currentEvent.eventDate && outerForm.eventDate.$invalid || outerForm.submitted && outerForm.eventDate.$invalid" class="required">{{getErrorMessage('eventDate')}}</span>
                 </td>
             </tr>
             <tr ng-if="selectedProgramStage.captureCoordinates">
@@ -71,29 +72,27 @@
             </span>-->
             <tr ng-repeat="eventGridColumn in eventGridColumns" ng-if="eventGridColumn.id !== 'comment' && eventGridColumn.id !== 'uid' && eventGridColumn.id !== 'event_date'">
                 <td >
-                    {{eventGridColumn.name}}                                    
+                    {{eventGridColumn.name}}<span ng-if="eventGridColumn.compulsory" class="required">*</span>
                 </td>
                 <td >
                     <ng-form name="innerForm">
                         <div ng-switch="eventGridColumn.type">
                             <div ng-switch-when="int">
-                                <input type="number" 
-                                        
+                                <input type="text"                                         
                                        d2-number-validation 
                                        number-type={{prStDes[eventGridColumn.id].dataElement.numberType}}
                                        ng-model="currentEvent[eventGridColumn.id]"                                                                
                                        ng-required={{eventGridColumn.compulsory}}
                                        name="foo" 
+                                       input-field-id={{eventGridColumn.id}} 
                                        style="width:99%;"/>
-                                <span ng-show="currentEvent[eventGridColumn.id] && innerForm.foo.$invalid" class="required">{{'value_must_be'| translate}} - {{prStDes[eventGridColumn.id].dataElement.numberType | translate}}</span>
                             </div>
                             <div ng-switch-when="string">                                
                                 <div class="container-fluid" ng-if="prStDes[eventGridColumn.id].dataElement.optionSet">
                                     <span ng-if="!selectedProgram.dataEntryMethod || optionSets[prStDes[eventGridColumn.id].dataElement.optionSet.id].options.length >= 7">
                                         <input type="text"
                                                class="typeahead"
-                                               placeholder="&#xf0d7;&nbsp;&nbsp;" 
-                                                
+                                               placeholder="&#xf0d7;&nbsp;&nbsp;"                                                 
                                                ng-model="currentEvent[eventGridColumn.id]"                                                                    
                                                typeahead="option.name as option.name for option in optionSets[prStDes[eventGridColumn.id].dataElement.optionSet.id].options | filter:$viewValue | limitTo:20" 
                                                typeahead-open-on-focus   
@@ -101,14 +100,14 @@
                                                d2-typeahead-validation
                                                ng-required={{eventGridColumn.compulsory}}
                                                name="foo" 
+                                               input-field-id={{eventGridColumn.id}} 
                                                style="width:99%;"/>
-                                        <span ng-show="innerForm.foo.$invalid" class="required">{{'option_required'| translate}}</span>
                                     </span>                                    
                                     <span ng-if="selectedProgram.dataEntryMethod && optionSets[prStDes[eventGridColumn.id].dataElement.optionSet.id].options.length < 7">                                    
                                         <label>
                                             <input type="radio" 
                                                    name="foo"  
-                                                     
+                                                   input-field-id={{eventGridColumn.id}}   
                                                    ng-required={{eventGridColumn.compulsory}}
                                                    ng-model="currentEvent[eventGridColumn.id]"
                                                    value=""> {{'no_value' | translate}}<br>
@@ -116,57 +115,53 @@
                                         <label ng-repeat="option in  optionSets[prStDes[eventGridColumn.id].dataElement.optionSet.id].options">
                                             <input type="radio" 
                                                    name={{eventGridColumn.id}}   
-                                                    
+                                                   input-field-id={{eventGridColumn.id}}    
                                                    ng-required={{eventGridColumn.compulsory}}
                                                    ng-model="currentEvent[eventGridColumn.id]"
                                                    value={{option.name}}> {{option.name}}<br>                                        
                                         </label>
-                                        <span ng-show="innerForm.foo.$invalid" class="required">{{'required'| translate}}</span>
                                     </span>
                                 </div>
                                 <div ng-if="!prStDes[eventGridColumn.id].dataElement.optionSet">
-                                    <input type="text" 
-                                            
+                                    <input type="text"                                             
                                         ng-model="currentEvent[eventGridColumn.id]"                                        
                                         ng-required={{eventGridColumn.compulsory}}
                                         name="foo" 
+                                        input-field-id={{eventGridColumn.id}} 
                                         style="width:99%;"/>
-                                    <span ng-show="innerForm.foo.$invalid" class="required">{{'string_required'| translate}}</span>
                                 </div>                                
                             </div>
                             <div ng-switch-when="bool">
-                                <select  
-                                        ng-model="currentEvent[eventGridColumn.id]"                                                                
+                                <select ng-model="currentEvent[eventGridColumn.id]"                                                                
                                         ng-required={{eventGridColumn.compulsory}}
                                         name="foo" 
+                                        input-field-id={{eventGridColumn.id}} 
                                         style="width:99%;">
                                 <option value="">{{'please_select'| translate}}</option>                        
                                 <option value="false">{{'no'| translate}}</option>
                                 <option value="true">{{'yes'| translate}}</option>
                                 </select>
-                                <span ng-show="innerForm.foo.$invalid" class="required">{{'bool_required'| translate}}</span>
                             </div>
                             <div ng-switch-when="date">
                                 <input type="text"                                                                
                                        placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
                                        d2-date
-                                       max-date="prStDes[eventGridColumn.id].allowFutureDate ? '' : 0" 
-                                        
+                                       max-date="prStDes[eventGridColumn.id].allowFutureDate ? '' : 0"                                         
                                        ng-model="currentEvent[eventGridColumn.id]"                                                               
                                        ng-required={{eventGridColumn.compulsory}}
                                        name="foo" 
+                                       input-field-id={{eventGridColumn.id}} 
                                        style="width:99%;"/>
-                                <span ng-show="innerForm.foo.$invalid" class="required">{{'date_required'| translate}}</span>
                             </div>
                             <div ng-switch-when="trueOnly">
-                                <input type="checkbox"          
-                                        
+                                <input type="checkbox"                                         
                                        ng-model="currentEvent[eventGridColumn.id]"                                                               
                                        ng-required={{eventGridColumn.compulsory}}
-                                       name="foo"/>
-                                <span ng-show="innerForm.foo.$invalid" class="required">{{'required'| translate}}</span>
+                                       name="foo" 
+                                       input-field-id={{eventGridColumn.id}} />
                             </div>
                         </div>
+                        <span ng-show="currentEvent[eventGridColumn.id] && innerForm.foo.$invalid || outerForm.submitted && innerForm.foo.$invalid" class="required">{{getErrorMessage(eventGridColumn.id)}}</span>
                     </ng-form>                    
                 </td>
             </tr>        
@@ -196,7 +191,7 @@
             </tr>
             <tr>
                 <td>
-                    {{selectedProgramStage.reportDateDescription ? selectedProgramStage.reportDateDescription : 'incident_date'| translate }}
+                    {{selectedProgramStage.reportDateDescription ? selectedProgramStage.reportDateDescription : 'incident_date'| translate }}<span class="required">*</span>
                 </td>
                 <td>
                     <input type="text"                                                   
@@ -207,8 +202,9 @@
                            ng-disabled="editingEventInFull"    
                            ng-required="true" 
                            name="eventDate" 
+                           input-field-id='eventDate' 
                            style="width:99%;">
-                    <span ng-show="outerForm.eventDate.$invalid" class="required">{{'date_required'| translate}}</span>
+                    <span ng-show="currentEvent.eventDate && outerForm.eventDate.$invalid || outerForm.submitted && outerForm.eventDate.$invalid" class="required">{{getErrorMessage('eventDate') | translate}}</span>
                 </td>
             </tr>
             <tr ng-if="selectedProgramStage.captureCoordinates">
@@ -259,29 +255,27 @@
                 <tbody id="list">       
                     <tr ng-repeat="de in section.programStageDataElements">
                         <td >
-                            {{prStDes[de.dataElement.id].dataElement.formName ? prStDes[de.dataElement.id].dataElement.formName : prStDes[de.dataElement.id].dataElement.name}}
+                            {{prStDes[de.dataElement.id].dataElement.formName ? prStDes[de.dataElement.id].dataElement.formName : prStDes[de.dataElement.id].dataElement.name}}<span ng-if="prStDes[de.dataElement.id].compulsory" class="required">*</span>
                         </td>
                         <td >
                             <ng-form name="innerForm">
                                 <div ng-switch="prStDes[de.dataElement.id].dataElement.type">
                                     <div ng-switch-when="int">
-                                        <input type="number" 
-                                                
+                                        <input type="number"                                                
                                                d2-number-validation 
                                                number-type={{prStDes[de.dataElement.id].dataElement.numberType}}
                                                ng-model="currentEvent[de.dataElement.id]"                                                                
                                                ng-required={{prStDes[de.dataElement.id].compulsory}}
                                                name="foo" 
+                                               input-field-id={{de.dataElement.id}} 
                                                style="width:99%;"/>
-                                        <span ng-show="innerForm.foo.$invalid" class="required">{{'value_must_be'| translate}} - {{prStDes[de.dataElement.id].dataElement.numberType | translate}}</span>
                                     </div>
                                     <div ng-switch-when="string">
                                         <div class="container-fluid" ng-if="prStDes[de.dataElement.id].dataElement.optionSet">                                
                                             <span ng-if="!selectedProgram.dataEntryMethod || optionSets[prStDes[de.dataElement.id].dataElement.optionSet.id].options.length > 8">
                                                 <input type="text"
                                                        class="typeahead"
-                                                       placeholder="&#xf0d7;&nbsp;&nbsp;" 
-                                                        
+                                                       placeholder="&#xf0d7;&nbsp;&nbsp;"                                                         
                                                        ng-model="currentEvent[de.dataElement.id]"                                                                    
                                                        typeahead="option.name as option.name for option in optionSets[prStDes[de.dataElement.id].dataElement.optionSet.id].options | filter:$viewValue | limitTo:20" 
                                                        typeahead-open-on-focus   
@@ -289,72 +283,68 @@
                                                        d2-typeahead-validation
                                                        ng-required={{prStDes[de.dataElement.id].compulsory}}
                                                        name="foo" 
+                                                       input-field-id={{de.dataElement.id}} 
                                                        style="width:99%;"/>
-                                                <span ng-show="innerForm.foo.$invalid" class="required">{{'option_required'| translate}}</span>
                                             </span>
                                             <span ng-if="selectedProgram.dataEntryMethod && prStDes[de.dataElement.id].dataElement.optionSet.options.length < 7">                                    
                                                 <label>
                                                     <input type="radio" 
-                                                           name="foo" 
-                                                            
+                                                           name="foo"  
+                                                           input-field-id={{de.dataElement.id}} 
                                                            ng-required={{prStDes[de.dataElement.id].compulsory}}
                                                            ng-model="currentEvent[de.dataElement.id]"
                                                            value=""> {{'no_value' | translate}}<br>
                                                 </label>                                    
                                                 <label ng-repeat="option in optionSets[prStDes[de.dataElement.id].dataElement.optionSet.id].options">
                                                     <input type="radio" 
-                                                           name={{de.dataElement.id}} 
-                                                            
+                                                           name={{de.dataElement.id}}   
+                                                           input-field-id={{de.dataElement.id}} 
                                                            ng-required={{prStDes[de.dataElement.id].compulsory}}
                                                            ng-model="currentEvent[de.dataElement.id]"
                                                            value={{option.name}}> {{option.name}}<br>                                        
                                                 </label>
-                                                <span ng-show="innerForm.foo.$invalid" class="required">{{'required'| translate}}</span>
                                             </span>
                                         </div>
                                         <div ng-if="!prStDes[de.dataElement.id].dataElement.optionSet">
-                                            <input type="text" 
-                                                 
+                                            <input type="text"                                                  
                                                 ng-model="currentEvent[de.dataElement.id]"
                                                 ng-required={{prStDes[de.dataElement.id].compulsory}}
                                                 name="foo" 
+                                                input-field-id={{de.dataElement.id}} 
                                                 style="width:99%;"/>
-                                            <span ng-show="innerForm.foo.$invalid" class="required">{{'string_required'| translate}}</span>
                                         </div>
                                     </div>
                                     <div ng-switch-when="bool">
-                                        <select  
-                                                ng-model="currentEvent[de.dataElement.id]"                                                                
+                                        <select ng-model="currentEvent[de.dataElement.id]"                                                                
                                                 ng-required={{prStDes[de.dataElement.id].compulsory}}
                                                 name="foo" 
+                                                input-field-id={{de.dataElement.id}} 
                                                 style="width:99%;">
                                         <option value="">{{'please_select'| translate}}</option>                        
                                         <option value="false">{{'no'| translate}}</option>
                                         <option value="true">{{'yes'| translate}}</option>
                                         </select>
-                                        <span ng-show="innerForm.foo.$invalid" class="required">{{'bool_required'| translate}}</span>
                                     </div>
                                     <div ng-switch-when="date">
                                         <input type="text"                                                                
                                                placeholder="{{dhis2CalendarFormat.keyDateFormat}}"
                                                d2-date
-                                               max-date="prStDes[de.dataElement.id].allowFutureDate ? '' : 0" 
-                                                
+                                               max-date="prStDes[de.dataElement.id].allowFutureDate ? '' : 0"                                                 
                                                ng-model="currentEvent[de.dataElement.id]"                                                               
                                                ng-required={{prStDes[de.dataElement.id].compulsory}}
                                                name="foo" 
+                                               input-field-id={{de.dataElement.id}} 
                                                style="width:99%;"/>
-                                        <span ng-show="innerForm.foo.$invalid" class="required">{{'date_required'| translate}}</span>
                                     </div>
                                     <div ng-switch-when="trueOnly">
-                                        <input type="checkbox"       
-                                                 
+                                        <input type="checkbox"                                                  
                                                ng-model="currentEvent[de.dataElement.id]"                                                               
                                                ng-required={{prStDes[de.dataElement.id].compulsory}}
-                                               name="foo"/>
-                                        <span ng-show="innerForm.foo.$invalid" class="required">{{'required'| translate}}</span>
+                                               name="foo" 
+                                               input-field-id={{de.dataElement.id}}/>
                                     </div>
                                 </div>
+                                <span ng-show="currentEvent[de.dataElement.id] && innerForm.foo.$invalid || outerForm.submitted && innerForm.foo.$invalid" class="required">{{getErrorMessage(de.dataElement.id)}}</span>
                             </ng-form>                            
                         </td>
                     </tr>
@@ -401,7 +391,7 @@
         <button ng-click="addEvent()" class="button not-printable">{{'save_and_back'| translate}}</button>
         <button ng-click="showEventList(null)" class="button not-printable">{{'go_back'| translate}}</button>        
     </span>
-    <span ng-if="outerForm.$invalid && formIsChanged()" class="horizontal-spacing red">{{'form_invalid' | translate}}</span>    
+    <span ng-if="isFormInvalid()" class="horizontal-spacing red">{{'form_invalid' | translate}}</span>
     
 </div>
-<!-- buttons for event registration / update ends -->
\ No newline at end of file
+<!-- buttons for event registration / update ends -->

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/views/ec-custom-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/views/ec-custom-form.html	2014-12-09 23:24:16 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/views/ec-custom-form.html	2015-01-13 17:31:37 +0000
@@ -19,8 +19,9 @@
                    ng-model="currentEvent.eventDate"
                    ng-disabled="editingEventInFull"    
                    ng-required="true" 
+                   input-field-id="eventDate"
                    name="eventDate"/>
-            <span ng-show="outerForm.submitted && outerForm.eventDate.$invalid" class="required">{{'date_required'| translate}}</span>
+            <span ng-show="currentEvent.eventDate && outerForm.eventDate.$invalid || outerForm.submitted && outerForm.eventDate.$invalid" class="required">{{getErrorMessage('eventDate')}}</span>
         </td>
     </tr>
     <tr ng-if="selectedProgramStage.captureCoordinates">
@@ -37,7 +38,7 @@
                    max="90"
                    ng-required="false"
                    style="min-width:128px"/>
-                <span ng-show="outerForm.submitted && outerForm.latitude.$invalid" class="required">{{'int_required'| translate}} [-90 ... 90]</span>
+                <span ng-show="outerForm.latitude.$invalid" class="required">{{'int_required'| translate}} [-90 ... 90]</span>
             </span>
             <span class="coordinate-container">
                 <input type="number"
@@ -48,7 +49,7 @@
                    max="180"
                    ng-required="false"
                    style="min-width:128px"/>
-                <span ng-show="outerForm.submitted && outerForm.longitude.$invalid" class="required">{{'int_required'| translate}}[-180 ... 180]</span>
+                <span ng-show="outerForm.longitude.$invalid" class="required">{{'int_required'| translate}}[-180 ... 180]</span>
             </span>
             <!--<span class='pull-right'>
                 <a href ng-click="showMap(currentEvent)" title="{{'get_from_map'| translate}}"><i class="fa fa-map-marker fa-2x"></i></a>
@@ -56,7 +57,8 @@
         </td>
     </tr>
 </table>
-<div ng-include="'../dhis-web-commons/customform/custom-form.html'"></div>  
+<div ng-include="'../dhis-web-commons/customform/custom-form.html'"></div>
+
 <div class="clear">
     <hr>
     <h4>
@@ -82,6 +84,7 @@
         </table>  
     </div>    
 </div>
+
 <!-- buttons for event registration / update begins -->
 <div style="clear: both;">                        
     <span ng-if="editingEventInFull">
@@ -93,6 +96,6 @@
         <button ng-click="addEvent()" class="button not-printable">{{'save_and_back'| translate}}</button>
         <button ng-click="showEventList(null)" class="button not-printable">{{'go_back'| translate}}</button>
     </span>
-    <span ng-if="outerForm.submitted && outerForm.$invalid" class="horizontal-spacing red">{{'form_invalid' | translate}}</span>
+    <span ng-if="isFormInvalid()" class="horizontal-spacing red">{{'form_invalid' | translate}}</span>
 </div>
 <!-- buttons for event registration / update ends -->
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/angular/plugins/dhis2/directives.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/angular/plugins/dhis2/directives.js	2015-01-12 18:24:10 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/angular/plugins/dhis2/directives.js	2015-01-13 17:31:37 +0000
@@ -4,8 +4,6 @@
 
 var d2Directives = angular.module('d2Directives', [])
 
-
-
 .directive('inputValidator', function() {
     
     return {
@@ -86,7 +84,7 @@
     };
 })
 
-.directive('d2NumberValidation', function() {
+.directive('d2NumberValidation', function(ErrorMessageService, $translate) {
     
     return {
         require: 'ngModel',
@@ -117,28 +115,49 @@
                 return isValid;
             }
             
-            var fieldName = element.attr('name');
+            var errorMessages = ErrorMessageService.getErrorMessages();
+            var fieldName = attrs.inputFieldId;
             var numberType = attrs.numberType;
             var isRequired = attrs.ngRequired === 'true';
             
             ctrl.$parsers.unshift(function(value) {
             	if(value){
-                    var isValid = checkValidity(numberType, value);
-                	ctrl.$setValidity(fieldName, isValid);                    
-                	//return isValid ? value : undefined;
+                    var isValid = checkValidity(numberType, value);                    
+                    if(!isValid){
+                        errorMessages[fieldName] = $translate('value_must_be_' + numberType);
+                    }
+                    else{
+                        if(isRequired){
+                            errorMessages[fieldName] = $translate('required');
+                        }
+                        else{
+                            errorMessages[fieldName] = "";
+                        }
+                    }
+                    
+                    ErrorMessageService.setErrorMessages(errorMessages);
+                	ctrl.$setValidity(fieldName, isValid);
                     return value;
                 }
                 
-                if(value === '' && !isRequired){
-                    ctrl.$setValidity(fieldName, true);                    
+                if(value === ''){
+                    if(isRequired){
+                        errorMessages[fieldName] = $translate('required');
+                    }
+                    else{
+                        ctrl.$setValidity(fieldName, true);
+                        errorMessages[fieldName] = "";
+                    }
+                    
+                    ErrorMessageService.setErrorMessages(errorMessages);
                     return undefined;
-                }                
+                }              
             });
            
             ctrl.$formatters.unshift(function(value) {                
                 if(value){
                     var isValid = checkValidity(numberType, value);
-                    ctrl.$setValidity(fieldName, isValid);                    
+                    ctrl.$setValidity(fieldName, isValid);
                     return value;
                 }
             });
@@ -152,8 +171,7 @@
         require: ['typeahead', 'ngModel'],
         link: function (scope, element, attr, ctrls) {
             element.bind('focus', function () {
-                ctrls[0].getMatchesAsync(ctrls[1].$viewValue);
-                
+                ctrls[0].getMatchesAsync(ctrls[1].$viewValue);                
                 scope.$watch(attr.ngModel, function(value) {
                     if(value === '' || angular.isUndefined(value)){
                         ctrls[0].getMatchesAsync(ctrls[1].$viewValue);
@@ -453,13 +471,14 @@
     };
 })
 
-.directive('d2Date', function(DateUtils, CalendarService, storage, $parse) {
+.directive('d2Date', function(DateUtils, CalendarService, ErrorMessageService, $translate, $parse) {
     return {
         restrict: 'A',
         require: 'ngModel',        
         link: function(scope, element, attrs, ctrl) {    
             
-            var fieldName = element.attr('name');
+            var errorMessages = ErrorMessageService.getErrorMessages();
+            var fieldName = attrs.inputFieldId;
             var isRequired = attrs.ngRequired === 'true';
             var calendarSetting = CalendarService.getSetting();            
             var dateFormat = 'yyyy-mm-dd';
@@ -485,27 +504,46 @@
                     $(this).change();
                 }
             })
-            .change(function() {
-                if(this.value){
+            .change(function() {                
+                if(this.value){                    
                     var rawDate = this.value;
                     var convertedDate = DateUtils.format(this.value);
 
                     var isValid = rawDate == convertedDate;
                     
+                    if(!isValid){
+                        errorMessages[fieldName] = $translate('date_required');
+                    }
+                    
                     if(isValid && maxDate === 0){                    
                         isValid = !moment(convertedDate, calendarSetting.momentFormat).isAfter(DateUtils.getToday());
-                    }
-                    
+                        if(!isValid){
+                            errorMessages[fieldName] = $translate('future_date_not_allowed');                            
+                        }
+                        else{                            
+                            if(isRequired){
+                                errorMessages[fieldName] = $translate('required');
+                            }
+                            else{
+                                errorMessages[fieldName] = "";
+                            }
+                        }
+                    }                    
                     ctrl.$setViewValue(this.value);
                     ctrl.$setValidity(fieldName, isValid);
                 }
                 else{
                     if(!isRequired){
                         ctrl.$setViewValue(this.value);
-                        ctrl.$setValidity(fieldName, !isRequired);                        
+                        ctrl.$setValidity(fieldName, !isRequired);
+                        errorMessages[fieldName] = "";
+                    }
+                    else{
+                        errorMessages[fieldName] = $translate('required');                        
                     }
                 }
                 
+                ErrorMessageService.setErrorMessages(errorMessages);
                 this.focus();
                 scope.$apply();
             });    

=== modified file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/angular/plugins/dhis2/services.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/angular/plugins/dhis2/services.js	2015-01-08 09:11:32 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/angular/plugins/dhis2/services.js	2015-01-13 17:31:37 +0000
@@ -212,20 +212,18 @@
                             attributes['name'] = deId;
                         }
                         
-                        var maxDate = programStageDataElements[deId].allowFutureDate ? '' : 0;
-                        
                         //check data element type and generate corresponding angular input field
                         if(programStageDataElements[deId].dataElement.type == "int"){
-                            newInputField = '<input type="number" ' +
+                            newInputField = '<input type="text" ' +
                                             this.getAttributesAsString(attributes) +
                                             ' d2-validation ' +
                                             ' d2-number-validation ' +
                                             ' number-type="' + programStageDataElements[deId].dataElement.numberType + '" ' +
                                             ' ng-model="currentEvent.' + deId + '"' +
+                                            ' input-field-id=" ' + deId + '"' +
                                             ' ng-class="getInputNotifcationClass(prStDes.' + deId + '.dataElement.id,true)"' +
                                             ' ng-blur="saveDatavalue(prStDes.'+ deId + ')"' + 
-                                            ' ng-required="prStDes.' + deId + '.compulsory"> ' + 
-                                            '<span ng-show="outerForm.submitted && outerForm.'+ deId +'.$invalid" class="required">{{\'value_must_be\'| translate}} - {{ "' + programStageDataElements[deId].dataElement.numberType + '" | translate}}</span>';                                     
+                                            ' ng-required="prStDes.' + deId + '.compulsory"> ';                                     
                         }
                         if(programStageDataElements[deId].dataElement.type == "string"){
                             if(programStageDataElements[deId].dataElement.optionSet){
@@ -234,6 +232,7 @@
                                             this.getAttributesAsString(attributes) +
                                             ' d2-validation ' +
                                             ' ng-model="currentEvent.' + deId + '" ' +
+                                            ' input-field-id=" ' + deId + '"' +
                                             ' ng-disabled="currentEvent[uid] == \'uid\'" ' +
                                             ' ng-required="prStDes.' + deId + '.compulsory"' +
                                             ' typeahead="option.name as option.name for option in optionSets.'+optionSetId+'.options | filter:$viewValue | limitTo:20"' +
@@ -243,19 +242,18 @@
                                             ' placeholder="&#xf0d7;&nbsp;&nbsp;" ' +
                                             ' ng-class="getInputNotifcationClass(prStDes.' + deId + '.dataElement.id,true)"' +                                            
                                             ' ng-blur="saveDatavalue(prStDes.'+ deId + ')"' +
-                                            ' typeahead-open-on-focus ng-required="prStDes.'+deId+'.compulsory"> ' +
-                                            '<span ng-show="outerForm.submitted && outerForm.'+ deId +'.$invalid" class="required">{{\'option_required\'| translate}}</span>';
+                                            ' typeahead-open-on-focus ng-required="prStDes.'+deId+'.compulsory"> ';
                         	}
                         	else{
                         		newInputField = '<input type="text" ' +
                                             this.getAttributesAsString(attributes) +
                                             ' d2-validation ' +
                                             ' ng-model="currentEvent.' + deId + '" ' +
+                                            ' input-field-id=" ' + deId + '"' +
                                             ' ng-disabled="currentEvent[uid] == \'uid\'" ' +
                                             ' ng-class="getInputNotifcationClass(prStDes.' + deId + '.dataElement.id,true)"' +
                                             ' ng-blur="saveDatavalue(prStDes.'+ deId + ')"' +
-                                            ' ng-required="prStDes.' + deId + '.compulsory"> ' +
-                                            '<span ng-show="outerForm.submitted && outerForm.'+ deId +'.$invalid" class="required">{{\'string_required\'| translate}}</span>';                                     
+                                            ' ng-required="prStDes.' + deId + '.compulsory"> ';                                     
                         	}
                         }
                         if(programStageDataElements[deId].dataElement.type == "bool"){
@@ -263,38 +261,41 @@
                                             this.getAttributesAsString(attributes) +
                                             ' d2-validation ' +
                                             ' ng-model="currentEvent.' + deId + '" ' +
+                                            ' input-field-id=" ' + deId + '"' +
                                             ' ng-class="getInputNotifcationClass(prStDes.' + deId + '.dataElement.id,true)"' +
                                             ' ng-change="saveDatavalue(prStDes.'+ deId + ')"' + 
                                             ' ng-required="prStDes.' + deId + '.compulsory">' + 
                                             '<option value="">{{\'please_select\'| translate}}</option>' +
                                             '<option value="false">{{\'no\'| translate}}</option>' + 
                                             '<option value="true">{{\'yes\'| translate}}</option>' +
-                                            '</select> ' +
-                                            '<span ng-show="outerForm.submitted && outerForm.'+ deId +'.$invalid" class="required">{{\'bool_required\'| translate}}</span>';                                     
+                                            '</select> ';                                     
                         }
                         if(programStageDataElements[deId].dataElement.type == "date"){
+                        	var maxDate = programStageDataElements[deId].allowFutureDate ? '' : 0;
                             newInputField = '<input type="text" ' +
                                             this.getAttributesAsString(attributes) +
                                             ' d2-validation ' +
                                             ' ng-model="currentEvent.' + deId + '"' +
+                                            ' input-field-id=" ' + deId + '"' +
                                             ' d2-date ' +
                                             ' max-date="' + maxDate + '"' + '\'' +
                                             ' ng-class="getInputNotifcationClass(prStDes.' + deId + '.dataElement.id,true)"' +
                                             ' blur-or-change="saveDatavalue(prStDes.'+ deId + ')"' + 
-                                            ' ng-required="prStDes.' + deId + '.compulsory"> ' +
-                                            '<span ng-show="outerForm.submitted && outerForm.'+ deId +'.$invalid" class="required">{{\'date_required\'| translate}}</span>'; 
+                                            ' ng-required="prStDes.' + deId + '.compulsory"> '; 
                         }
                         if(programStageDataElements[deId].dataElement.type == "trueOnly"){
                             newInputField = '<input type="checkbox" ' +
                                             this.getAttributesAsString(attributes) +
                                             ' d2-validation ' +
                                             ' ng-model="currentEvent.' + deId + '"' +
+                                            ' input-field-id=" ' + deId + '"' +
                                             ' ng-class="getInputNotifcationClass(prStDes.' + deId + '.dataElement.id,true)"' +
                                             ' ng-change="saveDatavalue(prStDes.'+ deId + ')"' +
-                                            ' ng-required="prStDes.' + deId + '.compulsory"> ' +
-                                            '<span ng-show="outerForm.submitted && outerForm.'+ deId +'.$invalid" class="required">{{\'required\'| translate}}</span>';
+                                            ' ng-required="prStDes.' + deId + '.compulsory"> ';
                         }
-
+						
+						newInputField = newInputField + '<span ng-show="outerForm.submitted && outerForm.'+ deId +'.$invalid || currentEvent.' + deId + ' && outerForm.'+ deId +'.$invalid" class="required">{{getErrorMessage(prStDes.' + deId + '.dataElement.id)}}</span>';                                        
+						
                         htmlCode = htmlCode.replace(inputField, newInputField);
                     }
                 }