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:
<!-- The custom widget template is
defined here
- You can
use standard HTML tags and AngularJS built
-in directives
, scope and interpolation
system - Custom widget properties
defined on the right can be used
as variables in a templates with properties
.newProperty
- Functions exposed in the controller can be used with ctrl.newFunction()
-->
<div ng-class="{
'form-horizontal': properties.labelPosition === 'left'&& !properties.labelHidden,
'row': properties.labelPosition === 'top'&& !properties.labelHidden || properties.labelHidden
}">
<div class="form-group">
<label
ng-if="!properties.labelHidden"
ng-class="{ 'control-label--required': properties.required }"
class="control-label col-xs-{{ !properties.labelHidden && properties.labelPosition === 'left' ? properties.labelWidth : 12 }}">
{{ properties.label | uiTranslate }}
</label>
<div class="col-xs-{{ 12 - (!properties.labelHidden && properties.labelPosition === 'left' ? properties.labelWidth : 0) }}">
<input
type="text"
class="form-control {{ properties.inputType }}"
placeholder="{{ properties.placeholder | uiTranslate }}"
ng-model="properties.value"
name="{{ ctrl.name }}"
sirensiret
ng-required="properties.required"
ng-readonly="properties.readOnly">
<div ng-messages="$form[ctrl.name].$dirty&& $form[ctrl.name].$error" ng-messages-include="forms-generic-errors.html" role="alert">
<div class="text-danger" ng-show="$form[ctrl.name].$error.integer">Cette valeur n'est pas un nombre valide</div>
<div class="text-danger" ng-show="$form[ctrl.name].$error.inputLength">La longueur d'un {{ properties.inputType | uppercase }} est de {{ ctrl.inputLen }} chiffres.</div>
<div class="text-danger" ng-show="$form[ctrl.name].$error.sirenSiretKey">La clé de validation de ce {{ properties.inputType | uppercase }} est invalide</div>
</div>
</div>
</div>
</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:
/**
* The controller is a JavaScript function that augments the AngularJS scope and exposes functions that can be used in the custom widget template
*
* Custom widget properties defined on the right can be used as variables in a controller with $scope.properties
* To use AngularJS standard services, you must declare them in the main function arguments.
*
* You can leave the controller empty if you do not need it.
*/
function($scope,$log, widgetNameFactory){
this.name = widgetNameFactory.getName($scope.properties.inputType);
this.inputLen =$scope.properties.inputType =='siren' ? 9:14;
$log.error ("tiersSirenSiretInput controler is here to serve!");
if(!$scope.properties.isBound('value')){
$log.error('the pbInput property named "value" need to be bound to a variable');
}
}
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:
var app = angular.module ('bonitasoft.ui.extensions',[]);
var INTEGER_REGEXP =/^\-?\d+$/;
var SIREN_REGEXP =/^\d{9}$/;
var SIRET_REGEXP =/^\d{14}$/;
app.directive('sirensiret',function(){
return{
require:'ngModel',
link:function(scope
, elm
, attrs
, ctrl
){
function validateSirenSiret(ngModelValue, ngViewValue){
// Default is OK.
ctrl.$setValidity('integer',true);
ctrl.$setValidity('inputLength',true);
ctrl.$setValidity('sirenSiretKey',true);
// non empty => make additional controls!
if(!ctrl.$isEmpty(ngModelValue)){
// Only numbers
ctrl.$setValidity('integer', INTEGER_REGEXP.test(ngModelValue));
// Check length
var isSiren = angular.element(elm).hasClass('siren');
var lengthOk =false;
if(isSiren){
lengthOk = SIREN_REGEXP.test(ngModelValue);
}else{
lengthOk = SIRET_REGEXP.test(ngModelValue);
}
ctrl.$setValidity('inputLength', lengthOk);
// Check key
if(lengthOk){
var nb
= ngModelValue
.split(''); var i;
var check =0;
var x;
for(i =0; i < nb.length; i++){
x = nb[nb.length - i -1]*((i %2)+1);
if(x <10){
check += x;
}else{
check
+= Math
.floor(x
/10)+(x
%10);}
}
if((check %10)!=0){
ctrl.$setValidity('sirenSiretKey',false);
}
}
}
// Return ngModelValue to display it
return ngModelValue;
}
ctrl.$parsers.push(validateSirenSiret);
}
};
});
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.