Quantcast
Channel: Bonitasoft Community - Bonita 7
Viewing all articles
Browse latest Browse all 34

Custom widget to validate user input

$
0
0

Not really a question but I could not find a lot of information in the documentation or the existing contributed projects.

I have a form with SIREN / SIRET fields (those are identification numbers for french companies). A siren is 9 digits and a siret is 14. I could easily use standard widgets for the length but that was all.

So I created a custom widget starting with pbInput standard widget (workspace/default/web_widgets). I removed minLength, maxLength, type, min value, max value and and added an inputType property which value can be siren or siret (default: siren).

The template I used is derivated from pbInput:

  1. <!-- The custom widget template is defined here
  2. - You can use standard HTML tags and AngularJS built-in directives, scope and interpolation system
  3. - Custom widget properties defined on the right can be used as variables in a templates with properties.newProperty
  4. - Functions exposed in the controller can be used with ctrl.newFunction()
  5. -->
  6.  
  7. <div ng-class="{
  8. 'form-horizontal': properties.labelPosition === 'left'&& !properties.labelHidden,
  9. 'row': properties.labelPosition === 'top'&& !properties.labelHidden || properties.labelHidden
  10. }">
  11. <div class="form-group">
  12. <label
  13. ng-if="!properties.labelHidden"
  14. ng-class="{ 'control-label--required': properties.required }"
  15. class="control-label col-xs-{{ !properties.labelHidden && properties.labelPosition === 'left' ? properties.labelWidth : 12 }}">
  16. {{ properties.label | uiTranslate }}
  17. </label>
  18. <div class="col-xs-{{ 12 - (!properties.labelHidden && properties.labelPosition === 'left' ? properties.labelWidth : 0) }}">
  19. <input
  20. type="text"
  21. class="form-control {{ properties.inputType }}"
  22. placeholder="{{ properties.placeholder | uiTranslate }}"
  23. ng-model="properties.value"
  24. name="{{ ctrl.name }}"
  25. sirensiret
  26. ng-required="properties.required"
  27. ng-readonly="properties.readOnly">
  28. <div ng-messages="$form[ctrl.name].$dirty&& $form[ctrl.name].$error" ng-messages-include="forms-generic-errors.html" role="alert">
  29. <div class="text-danger" ng-show="$form[ctrl.name].$error.integer">Cette valeur n'est pas un nombre valide</div>
  30. <div class="text-danger" ng-show="$form[ctrl.name].$error.inputLength">La longueur d'un {{ properties.inputType | uppercase }} est de {{ ctrl.inputLen }} chiffres.</div>
  31. <div class="text-danger" ng-show="$form[ctrl.name].$error.sirenSiretKey">La clé de validation de ce {{ properties.inputType | uppercase }} est invalide</div>
  32. </div>
  33. </div>
  34. </div>
  35. </div>

You will note the following changes: I added some new messages to the ng-messages div and added a "sirensiret" directive. I also added a class to detect input type.

The controller is really simple:

  1. /**
  2.   * The controller is a JavaScript function that augments the AngularJS scope and exposes functions that can be used in the custom widget template
  3.   *
  4.   * Custom widget properties defined on the right can be used as variables in a controller with $scope.properties
  5.   * To use AngularJS standard services, you must declare them in the main function arguments.
  6.   *
  7.   * You can leave the controller empty if you do not need it.
  8.   */
  9. function($scope,$log, widgetNameFactory){
  10.  
  11. this.name = widgetNameFactory.getName($scope.properties.inputType);
  12. this.inputLen =$scope.properties.inputType =='siren' ? 9:14;
  13.  
  14. $log.error ("tiersSirenSiretInput controler is here to serve!");
  15.  
  16. if(!$scope.properties.isBound('value')){
  17. $log.error('the pbInput property named "value" need to be bound to a variable');
  18. }
  19. }

This is almost the standard controller. I just initialize the default length for error messages.

Then I added a custom asset checkSirenSiret.js that add some directives to bonitasoft.ui.extensions:

  1. var app = angular.module ('bonitasoft.ui.extensions',[]);
  2.  
  3. var INTEGER_REGEXP =/^\-?\d+$/;
  4. var SIREN_REGEXP =/^\d{9}$/;
  5. var SIRET_REGEXP =/^\d{14}$/;
  6.  
  7. app.directive('sirensiret',function(){
  8. return{
  9. require:'ngModel',
  10. link:function(scope, elm, attrs, ctrl){
  11.  
  12. function validateSirenSiret(ngModelValue, ngViewValue){
  13.  
  14. // Default is OK.
  15. ctrl.$setValidity('integer',true);
  16. ctrl.$setValidity('inputLength',true);
  17. ctrl.$setValidity('sirenSiretKey',true);
  18.  
  19. // non empty => make additional controls!
  20. if(!ctrl.$isEmpty(ngModelValue)){
  21. // Only numbers
  22. ctrl.$setValidity('integer', INTEGER_REGEXP.test(ngModelValue));
  23.  
  24. // Check length
  25. var isSiren = angular.element(elm).hasClass('siren');
  26. var lengthOk =false;
  27.  
  28. if(isSiren){
  29. lengthOk = SIREN_REGEXP.test(ngModelValue);
  30. }else{
  31. lengthOk = SIRET_REGEXP.test(ngModelValue);
  32. }
  33.  
  34. ctrl.$setValidity('inputLength', lengthOk);
  35.  
  36. // Check key
  37. if(lengthOk){
  38. var nb = ngModelValue.split('');
  39. var i;
  40. var check =0;
  41. var x;
  42.  
  43. for(i =0; i < nb.length; i++){
  44. x = nb[nb.length - i -1]*((i %2)+1);
  45. if(x <10){
  46. check += x;
  47. }else{
  48. check += Math.floor(x /10)+(x %10);
  49. }
  50. }
  51.  
  52. if((check %10)!=0){
  53. ctrl.$setValidity('sirenSiretKey',false);
  54. }
  55. }
  56. }
  57.  
  58. // Return ngModelValue to display it
  59. return ngModelValue;
  60. }
  61.  
  62. ctrl.$parsers.push(validateSirenSiret);
  63. }
  64. };
  65. });

I use $parsers as it is a little bit more flexible than $validators (I would have to write multiple validators, I can use only one parser). Nothing special about this code: first check if the input is a number, then the length, then the Luhn key used to validate the input.

Hope this helps some others.


Viewing all articles
Browse latest Browse all 34

Trending Articles