Skip to Content

A common usecase is to have a form — or multiple forms — which should be validated upon clicking a “Submit” button. The UI5 validation mechanism by default does a terrific good job for inline validation (i.e. validating fields right after you have changed them, giving immediate feedback to the user) but this can lead to some issues.

Imagine a form with a couple of input fields which require a value. However, since they are initially empty, leaving them blank does not trigger a change event (and subsequent validation), so you could possibly submit your form without entering any data.

Or imagine you have a page with multiple forms, with input fields which depend on each other, and you want to validate all forms and their fields at once when clicking the “Submit” button.

Those cases are examples where after submit validation is more appropriate, and I will show how this can be achieved by still using the standard UI5 validation mechanism.

I have created a simple class which will validate your form(s) upon submitting it, and the implementation is pretty simple. But first, let me explain a bit about the standard validation mechanism.

To enable standard, inline validation of a control, you should do at least the following:

  1. Attach to the core’s validationError and validationSuccess error handlers
  2. Implement a contraint to the binding of your control

Attaching to the validation event handlers is pretty simple, and need only be done once (for instance in the view controller’s onInit() event handler):

    onInit: function() {

        // Attaches validation handlers

        sap.ui.getCore().attachValidationError(function (oEvent) {

            oEvent.getParameter(“element“).setValueState(ValueState.Error);

        });

        sap.ui.getCore().attachValidationSuccess(function (oEvent) {

            oEvent.getParameter(“element“).setValueState(ValueState.None);

        });

    }

Setting a constraint to a control is done using the UI5 Simple Types. For instance, to set an input field to required, you can simply use this:

    <Label text=”Name” required=”true” />

    <Input value=”{

        path : ‘/userName’,

        type : ‘sap.ui.model.type.String’,

        constraints : {

            minLength : 2

        }

    }“/>

You could even use regular expressions, for instance for validating an email address:

    <Label text=”Email” required=”true” />

    <Input value=”{

        path : ‘/userEmail’,

        type : ‘sap.ui.model.type.String’,

        constraints : {

            search : ‘\\S+@\\S+\\.\\S+’

        }

    }“/>

(The above example uses rather basic email validation, just a simple ‘lorem@ipsum.dolor’. Use a more robust regex productively!)

If you implement this in a UI5 page, and type a name of 1 character or supply an invalid email address, you will see the controls will be surrounded with a red border, and the validation error is displayed in a tooltip:

Screen Shot 2015-11-01 at 16.34.08.png

However, not entering anything will not trigger a validation…

I have created a simple class which takes care of the validation upon clicking a submit button, and the implementation is fairly simple:

    onSubmit: function() {

        // Create new validator instance

        var validator = new Validator();

        // Validate input fields against root page with id ‘somePage’

        if (validator.validate(this.byId(“somePage“))) {

            // perform the actual form submit here

        }

    }

You simply create a new instance of the class, and then call the only method of that class, ‘validate‘. The input parameter for that method is the ID of any page, control, or form you wish to set as the root container for your validation. The method returns ‘true’ if all fields are filled correctly (i.e., no constraints are violated), or false otherwise.

Below is a screenshot of the ‘validate’ method, and hopefully the inline comments explain everything that is happening:

Screen Shot 2015-11-01 at 16.59.28.png

In short:

  • Check if the supplied control is something that can be visible and actually is visible (no sense in checking anything that cannot be visible)
  • Then check if a constraint is set
  • If yes, then try validating the control as if it were an inline validation
  • However, if the control could not be validated, it may have aggregations that could be validated (i.e. it may be a form, a table, etc. which may hold child controls). We then check the children, and recursively call the validate method again

You can see a working demo of this ‘after-submit validation’ here.

The Validator code itself is available from GitHub. It is a simple, single file you may include in your project. If you find any bugs or room for improvement, please feel free to clone and submit any changes!

I hope some may find it useful!

To report this post you need to login first.

32 Comments

You must be Logged on to comment or reply to a post.

  1. Dheeram Kallem

    Good One, I worked on same requirement, but did the validations through a utility method and getting each control through ID and setting error state based on the value.

    You can check whether the control is enabled along with visible property.

    (0) 
  2. Frank Luzat

    Good one!

    This is a good utility for triggering the validations based on databinding and standard simple types.

    But for me these simple type validations are most times not sufficient or the standard value state messages are too general.

    An example:

    I have a datepikcer bound to an OData model field and type set to sap.ui.model.type.Date

    <Label text=”{i18n>ValidFrom}” required=”true” />

    <DatePicker value=”{path: ‘ValidFrom’, type: ‘sap.ui.model.type.Date’,

         formatOptions: { pattern: ‘yyyy-MM-dd’ } }”

         change=”onChangeFormField” />

     

    Since this is a required field in my form it mustn’t be empty.

    In the onChange event I check if the date is empty and call setValueState on the control.

    This seems to conflict with the standard validation triggered by the type assignment because I can see the red border appear for a very short time before it is removed again.

    This is because the type validation detects no error if no date is set.

    I think standard type validation doesn’t work if custom validation on the same control should also be used.

    Leaving the

    type: ‘sap.ui.model.type.Date’

    out of my control declaration also doesn’t work because then the date format yyyy-MM-dd is lost.

    So what to do??

    It would be perfect in my eyes if your validator would take a callback function as argument.

    This function could then be called if there is no type validation for a binding or if the type validation didn’t return any error.

    The callback function could check for which control it is called and do custom validations and return a custom value state message to your validator.

    What do you think?

    (0) 
    1. Robin van het Hof Post author

      Hi Frank,

      As far as I have experienced, all validations (which include required fields) could be handled using the simple types, even sap.ui.model.type.Date can be set to both a formatter and still be validated as required by setting the ‘constraint’ parameter:

      As you can see from the example I provided the DatePicker control is required too (although I forgot to include the ‘required’ property in the label, which is now fixed) because if you set a constraint:

      <DatePicker id=”myDate” value=”{

          path : ‘/date’,

          type : ‘sap.ui.model.type.Date’,

          formatOptions : {

              source: {

                  pattern: ‘dd-MM-yyyy’

              },

              pattern: ‘dd-MM-yyyy’

          },

          constraints : {

              minimum : ’01-01-1900′

          }

      }”/>

      …you now must enter a date of at least Jan 01, 1900, which in effect makes your DatePicker require an input. Same is valid for strings (for instance, { minlength : 1 }), numbers (for instance, { minimum : 1 }), currency (for instance, { minimum : 0.01 }), etc

      (1) 
      1. Frank Luzat

        Ok, makes sense.

        However, I can’t get the constraints to work with my datepicker.

        SAPUI5 SDK – Demo Kit

        The documentation says that

        • maximum (expects an date presented in the source-pattern format)
        • minimum (expects an date presented in the source-pattern format)

        I don’t know the exact source format since it comes from the OData model.

        I tried different things as source: { pattern: ‘….’ } but nothing worked.

        Any idea?

        Another point is the validation of dependencies between fields.

        I suppose you just do them “manually” after you call your validator class?

        Do then check whether a certain field may already have an error state set from the type based validation and then skip your advanced check?

        (0) 
        1. Robin van het Hof Post author

          Not sure about the OData pattern, but you may be able to determine it by just calling the service in a browser and see the date format…

          Validation dependency between fields (for instance, from-date must be before to-date, etc) I simply set a new constraint on both fields on the respective onChange events (so the from-date maximum is the to-date value, and the to-date minimum is the from-date value, etc)

          This way I ensure I still validate the ‘correct’ way and don’t need to do any manual validation — validating in multiple ways can lead to inconsistencies

          (0) 
          1. Frank Luzat

            The OData format of date values is internally mapped to Javascript Date objects in UI5.

            This one will set the minimum date to 1990-01-01

            <DatePicker
               value=”{path: ‘ValidFrom’,
               type: ‘sap.ui.model.type.Date’,
                  formatOptions: {
                      pattern: ‘yyyy-MM-dd’,
                      strictParsing: ‘true’,
                      UTC: ‘true’
                  },
                  constraints : {
                      minimum : 631148400000
                  }
              }” />


            But this will display a validation error which no one can understand:

            2015-12-07 10_19_41-SPOT.png

            And from your example: No user will understand this error message containing a regex.

            2015-12-07 10_20_18-Plunker.png

            Maybe we have to derive your own types for every use case where the validation messages of the standard simpe types don’t suffice.

            (1) 
  3. Matthias Kurz

    We use it and it works well. Now we try to disable button x when the form is invalid on init. That’s mostly the case, when someone clicks create new x. In this case oControl.getBinding(aValidateProperties[i]) in line 53 of validator.js hostet on github for all returns undefined and in the end isValid/() returns true. Is that intended? What can we do?

    (0) 
    1. Robin van het Hof Post author

      If getBinding returns undefined, it usually means the binding is not set. Could you try moving your logic to the ‘onAfterRendering()’ event handler instead of onInit()? The form is not available in the onInit() hook, which may cause this issue

      (0) 
        1. Robin van het Hof Post author

          That is weird, because regardless of where you call the validator function (in onAfterRendering or in a button’s press event handler), all controls should be available, and the instanceof should work just fine.

          However, this script was intended to be fired when clicking a button, and not as a listener (that’s where the standard ‘attachValidationError/Success’ handlers are for.

          If you need to set the state of your button when editing fields, and enable/disable the button directly when filling fields (and validating them), you could better set the state of your button depending on the state in the core’s ‘attachValidationZzz’ handlers

          (0) 
  4. Frank Luzat

    Hi Robin,

    another thing which I experienced using this validator:

    Having a sap.m.input which has a binding of type sap.ui.model.odata.type.Decimal thousand separators are automatically inserted (if configured this way). So passing this value

    “123,456.99” into validateValue gives an exception with message “Enter a number”.

    The value has to be parsed from external into internal format first:

    var oBinding = oControl.getBinding(aValidateProperties[i]);

    var oValue = oControl.getProperty(aValidateProperties[i];

    oValue = oBinding.getType().parseValue(oValue, oBinding.sInternalType);

    oBinding.getType().validateValue(oValue);

    (0) 
  5. Pranav Nagpal

    Great blog..

    Just a quick question, does this only works with Forms. I tried putting couple of input fields in VBox, attached the attachValidationError in init but for me, validations are never triggered.

    Thanks

    Pranav

    (0) 
    1. Jan Penninkhof

      The Validator is meant to be working from the Submit button, so you’ll have to add something like this:

           onSubmit: function() {

              // Create new validator instance

              var validator = new Validator();

              // Validate input fields against root page with id ‘somePage’

              if (validator.validate(this.byId(“somePage“))) {

                  // perform the actual form submit here

              }

          }

      Validation through the use of this class is only triggered by explicitly calling the validate method.

      (0) 
      1. Pranav Nagpal

        I was just trying to test the first part, where validation should trigger when i am typing something out of constraint. Example if my constraint has maximum field length 4 and I type 5 characters, on exit of input field should display error. 🙂 as working in example here. But somehow not working for me. I put a debug point, to see what is going wrong. My debugger never reached    oEvent.getParameter(“element“).setValueState(ValueState.Error);. May be event is never regiseterd.

        Thanks

        pranav

        (0) 
  6. N. van der Linden

    Hi Robin,

    really a nice feature! For now I decided to create my own validation using the “byId” option for the form fields though since I am still learning UI5 and want to know exactly what I am doing. But I might use it later because I really like what you build. 

    I succeeded in building the validation and when the user decides to use the back button I do a resetChanges() on the model to make sure their changes are ‘canceled’. However, when validation errors were previously shown and the user opens the form again the validation errors are still shown on the fields, how do you reset those? Or better said is there another way (like resetChanges on the model) to reset them all at once or do I need to call a validationReset function where I just reset each of the statuses manually back to ‘none’ when the user leaves the screen?

    Regards,

    Nico

    (0) 
  7. Melwyn Monis

    Hi Robin,

    I need to call the validate method twice in the controller for 2 different fields. I cannot pass a parent control id as it is a Tree Table which has rows and there are some restrictions in using the rows aggregations.(it doesn’t have a visible property)

    If in your example, I call the validate method twice for 2 different controls.

    e.g.

    validator.validate(this.byId(“myNum”));  //for the input field in Another Form

    validator.validate(this.byId(“myDate”));  //for the DatePicker in Another Form

    it validates both the controls but only highlights the last one (i.e datePicker) in red.

    Can you let me know what needs to be changed so that I can call the validate() for 2 or more controls. ?

    Regards,

    Melwyn.

    (0) 
  8. Noël Hendrikx

    Dear all,

    If you would like to use this validator function also inside your forms within a sap.m.ObjectPageLayout, you will have to expand the array “aPossibleAggregations” with the following three elements:

    “sections”, “subSections”, “_grid”

    var aPossibleAggregations = ["items", "content", "form", "formContainers", "formElements", "fields", "sections", "subSections", "_grid"],

     

    Happy validating 🙂

    Noël

    (1) 
  9. Andrew Cemprola

    Quick question, I’ve implemented your code but i’m having issues. After entering text into a field and removing focus from that input the text is immediately erased. No errors in the console. Even with all blank fields the Validator is still getting a value of True.  Is there a minimum version of UI5 i need to be running? is this a problem you’ve seen or heard of before?

    (0) 
    1. Robin van het Hof Post author

      Hi Andrew, the only time I see this happening is when your control is not correctly bound to your model (but that has nothing to do with my code since that will only be invoked after a button press, and should in no way modify your control contents)

      Can you check whether you have bound your controls correctly to your model?

      (0) 
  10. Tatiana Rastorgueva

    Hey Robin,

     

    Great job, thank you very much. However, I have some problems while trying validate the combobox. Can you please provide an example how to validate combobox/select element?

    (0) 
      1. Aparajitha Medavaram

        Hi Robin,

        Nice blog. It reduces developer efforts and saves time too.

        I just tried with Select control. But not able to achieve it. Following is the example:

        <Select selectedKey=”” id=”idSelect”>
        <core:Item key=”” text=””/>
        <core:Item key=”ABC” text=”ABC”/>
        <core:Item key=”DEF” text=”DEF”/>
        <layoutData>
        <l:GridData span=”L2 M2 S10″/>
        </layoutData>
        </Select>

        Even if selected key is empty, no error is thrown.

        Thanks.

        (0) 
        1. Robin van het Hof Post author

          Your code example is missing two things:

          1. Model binding
          2. A validation constraint

          Like all the other controls, this is mandatory.

           

          Your Select example should be something like this:

          <Select items="{/choices}" selectedKey="{ 
              path : '/mySelectedKey', 
              type : 'sap.ui.model.type.Integer', 
              constraints : { 
                  minimum : 1 
              }
          }">
              <core:Item key="{key}" text="{value}"/>
          </Select>
          

          (In this example, the list of items should have a key equal or greater than 1 to validate, any other value will give a validation error)

          (0) 

Leave a Reply