Let the platform do the work

Adding Field Validation to the Record View

Overview

This page explains how to add additional field validation to the record view. In the following examples, we will extend and override the stock Accounts record view to add custom validation. The custom validation will require the Office Phone field when the account type is set to "Customer" and also require the user to enter at least one email address.

Error Messages

When throwing a validation error, Sugar has several stock messages you may choose to use. They are shown below:

Error Key Label Description
maxValue ERROR_MAXVALUE This maximum value of this field
minValue ERROR_MINVALUE This minimum value of this field
maxLength ERROR_MAX_FIELD_LENGTH The max length of this field
minLength ERROR_MIN_FIELD_LENGTH The min length of this field
datetime ERROR_DATETIME This field requires a valid date
required ERROR_FIELD_REQUIRED This field is required
email ERROR_EMAIL There is an invalid email address
primaryEmail ERROR_PRIMARY_EMAIL No primary email address is set
duplicateEmail ERROR_DUPLICATE_EMAIL There is a duplicate email address
number ERROR_NUMBER This field requires a valid number
isBefore ERROR_IS_BEFORE The date of this field can not be after another date
isAfter ERROR_IS_AFTER The date of this field can not be before another date

You also have the option of displaying multiple error messages at a time. The example below would throw an error message notifying the user that the selected field is required and that it is also not a number.

errors['<field name>'] = errors['<field name>'] || {};
errors['<field name>'].required = true;
errors['<field name>'].number = true;

Custom Error Messages

Custom error message can be used by appending custom language keys to app.error.errorName2Keys when initializing an extended controller. To accomplish this, create a new language key in the $app_strings. An example of this is shown below:

./custom/Extension/application/Ext/Language/en_us.error_custom_message.php

<?php

$app_strings['ERROR_CUSTOM_MESSAGE'] = 'My custom error message.';

Next, you will need to update your controller to use the new language key. To accomplish this, add your custom language key to the app.error.errorName2Keys array in the initialize method:

initialize: function (options) {
    this._super('initialize', [options]); 

    //add custom message key
    app.error.errorName2Keys['custom_message'] = 'ERROR_CUSTOM_MESSAGE';

    ....
},

Once completed, you can call your custom message by setting the app.error.errorName2Keys entry to true as shown below:

errors['phone_office'] = errors['phone_office'] || {};
errors['phone_office'].custom_message = true;

Method 1: Extending the RecordView and CreateView Controllers

One way to validate fields on record view is by creating record and create view controllers. This method requires a duplication of code due to the hierarchy design, however, it does organize the code by module and extend the core functionality. To accomplish this, override and extend the create view controller. This handles the validation when a user is creating a new record. Once the controller has been properly extended, define the validation check and use the model.addValidationTask method to append your function to the save validation.

./custom/modules/Accounts/clients/base/views/create/create.js

({
    extendsFrom: 'CreateView',
    initialize: function (options) {

        this._super('initialize', [options]);

        //add validation tasks
        this.model.addValidationTask('check_account_type', _.bind(this._doValidateCheckType, this));
        this.model.addValidationTask('check_email', _.bind(this._doValidateEmail, this));
    },

    _doValidateCheckType: function(fields, errors, callback) {
        //validate type requirements
        if (this.model.get('account_type') == 'Customer' && _.isEmpty(this.model.get('phone_office')))
        {
            errors['phone_office'] = errors['phone_office'] || {};
            errors['phone_office'].required = true;
        }

        callback(null, fields, errors);
    },

    _doValidateEmail: function(fields, errors, callback) {
        //validate email requirements
        if (_.isEmpty(this.model.get('email')))
        {
            errors['email'] = errors['email'] || {};
            errors['email'].required = true;
        }

        callback(null, fields, errors);
    },
})

Next, duplicate the validation logic for the record view. This handles the validation when editing existing records.

./custom/modules/Accounts/clients/base/views/record/record.js

({
    /* because 'accounts' already has a record view, we need to extend it */
    extendsFrom: 'AccountsRecordView', 

    initialize: function (options) {

        this._super('initialize', [options]);

        //add validation tasks
        this.model.addValidationTask('check_account_type', _.bind(this._doValidateCheckType, this));
        this.model.addValidationTask('check_email', _.bind(this._doValidateEmail, this));
    },

    _doValidateCheckType: function(fields, errors, callback) {

        //validate requirements
        if (this.model.get('account_type') == 'Customer' && _.isEmpty(this.model.get('phone_office')))
        {
            errors['phone_office'] = errors['phone_office'] || {};
            errors['phone_office'].required = true;
        }

        callback(null, fields, errors);
    },

    _doValidateEmail: function(fields, errors, callback) {

        //validate email requirements
        if (_.isEmpty(this.model.get('email')))
        {
            errors['email'] = errors['email'] || {};
            errors['email'].required = true;
        }

        callback(null, fields, errors);
    },
})

Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. More information on displaying custom error messages can be found in the Error Messages section.

Method 2: Overriding the RecordView and CreateView Layouts

Another method for defining your own custom validation is to override a module's record and create layouts to append a new view with your logic. The benefits of this method are that you can use the single view to house the validation logic, however, this means that you will have to override the layout. When overriding a layout, verify that the layout has not changed when upgrading your instance. Once the layouts are overridden, define the validation check and use the model.addValidationTask method to append the function to the save validation. 

First, create the custom view. For the accounts example, create the view validate-account:

./custom/modules/Accounts/clients/base/views/validate-account/validate-account.js 

({
   className:"hidden",

   _render: function() {
       //No-op, Do nothing here
   },

   bindDataChange: function() {
       //add validation tasks
       this.model.addValidationTask('check_account_type', _.bind(this._doValidateCheckType, this));
       this.model.addValidationTask('check_email', _.bind(this._doValidateEmail, this));
   },

   _doValidateCheckType: function(fields, errors, callback) {
       //validate type requirements
       if (this.model.get('account_type') == 'Customer' && _.isEmpty(this.model.get('phone_office')))
       {
           errors['phone_office'] = errors['phone_office'] || {};
           errors['phone_office'].required = true;
       }

       callback(null, fields, errors);
   },

   _doValidateEmail: function(fields, errors, callback) {
       //validate email requirements
       if (_.isEmpty(this.model.get('email')))
       {
           errors['email'] = errors['email'] || {};
           errors['email'].required = true;
       }

       callback(null, fields, errors);
   },
})

More information on displaying custom error messages can be found in the Error Messages section. Next, depending on the selected module, duplicate its create layout to the modules custom folder to handle record creation. In our Accounts example, we have an existing ./modules/Accounts/clients/base/layouts/create/create.php file so we need to duplicate this file to ./custom/modules/Accounts/clients/base/layouts/create/create.php. After this has been completed, append the new custom view to the components. append:

array(
   'view' => 'validate-account',
),

As shown below:

./custom/modules/Accounts/clients/base/layouts/create/create.php

<?php

$viewdefs['Accounts']['base']['layout']['create'] = array(
    'components' =>
        array(
            array(
                'layout' =>
                    array(
                        'components' =>
                            array(
                                array(
                                    'layout' =>
                                        array(
                                            'components' =>
                                                array(
                                                    array(
                                                        'view' => 'create',
                                                    ),
                                                    array(
                                                        'view' => 'validate-account',
                                                    ),
                                                ),
                                            'type' => 'simple',
                                            'name' => 'main-pane',
                                            'span' => 8,
                                        ),
                                ),
                                array(
                                    'layout' =>
                                        array(
                                            'components' =>
                                                array(),
                                            'type' => 'simple',
                                            'name' => 'side-pane',
                                            'span' => 4,
                                        ),
                                ),
                                array(
                                    'layout' =>
                                        array(
                                            'components' =>
                                                array(
                                                    array(
                                                        'view' =>
                                                            array (
                                                                'name' => 'dnb-account-create',
                                                                'label' => 'DNB Account Create',
                                                            ),
                                                        'width' => 12,
                                                    ),
                                                ),
                                            'type' => 'simple',
                                            'name' => 'dashboard-pane',
                                            'span' => 4,
                                        ),
                                ),
                                array(
                                    'layout' =>
                                        array(
                                            'components' =>
                                                array(
                                                    array(
                                                        'layout' => 'preview',
                                                    ),
                                                ),
                                            'type' => 'simple',
                                            'name' => 'preview-pane',
                                            'span' => 8,
                                        ),
                                ),
                            ),
                        'type' => 'default',
                        'name' => 'sidebar',
                        'span' => 12,
                    ),
            ),
        ),
    'type' => 'simple',
    'name' => 'base',
    'span' => 12,
);

Lastly, depending on the selected module, duplicate its record layout to the modules custom folder to handle editing a record. In the accounts example, we do not have an existing ./modules/Accounts/clients/base/layouts/record/record.php file so we duplicated the core ./clients/base/layouts/record/record.php to ./custom/modules/Accounts/clients/base/layouts/record/record.php. Since we are copying from the ./clients/ core directory, modify:

$viewdefs['base']['layout']['record'] = array(
    ...
);

To:

$viewdefs['Accounts']['base']['layout']['record'] = array(
    ...
);

After this has been completed, append the new custom view to the components:

array(
    'view' => 'validate-account',
),

The resulting file is shown below:

./custom/modules/Accounts/clients/base/layouts/record/record.php

<?php

$viewdefs['Accounts']['base']['layout']['record'] = array(
    'components' => array(
        array(
            'layout' => array(
                'components' => array(
                    array(
                        'layout' => array(
                            'components' => array(
                                array(
                                    'view' => 'record',
                                    'primary' => true,
                                ),
                                array(
                                    'view' => 'validate-account',
                                ),
                                array(
                                    'layout' => 'extra-info',
                                ),
                                array(
                                    'layout' => array(
                                        'name' => 'filterpanel',
                                        'span' => 12,
                                        'last_state' => array(
                                            'id' => 'record-filterpanel',
                                            'defaults' => array(
                                                'toggle-view' => 'subpanels',
                                            ),
                                        ),
                                        'availableToggles' => array(
                                            array(
                                                'name' => 'subpanels',
                                                'icon' => 'icon-table',
                                                'label' => 'LBL_DATA_VIEW',
                                            ),
                                            array(
                                                'name' => 'list',
                                                'icon' => 'icon-table',
                                                'label' => 'LBL_LISTVIEW',
                                            ),
                                            array(
                                                'name' => 'activitystream',
                                                'icon' => 'icon-th-list',
                                                'label' => 'LBL_ACTIVITY_STREAM',
                                            ),
                                        ),
                                        'components' => array(
                                            array(
                                                'layout' => 'filter',
                                                'targetEl' => '.filter',
                                                'position' => 'prepend'
                                            ),
                                            array(
                                                'view' => 'filter-rows',
                                                "targetEl" => '.filter-options'
                                            ),
                                            array(
                                                'view' => 'filter-actions',
                                                "targetEl" => '.filter-options'
                                            ),
                                            array(
                                                'layout' => 'activitystream',
                                                'context' =>
                                                array(
                                                    'module' => 'Activities',
                                                ),
                                            ),
                                            array(
                                                'layout' => 'subpanels',
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                            'type' => 'simple',
                            'name' => 'main-pane',
                            'span' => 8,
                        ),
                    ),
                    array(
                        'layout' => array(
                            'components' => array(
                                array(
                                    'layout' => 'sidebar',
                                ),
                            ),
                            'type' => 'simple',
                            'name' => 'side-pane',
                            'span' => 4,
                        ),
                    ),
                    array(
                        'layout' => array(
                            'components' => array(
                                array(
                                    'layout' => array(
                                        'type' => 'dashboard',
                                        'last_state' => array(
                                            'id' => 'last-visit',
                                        )
                                    ),
                                    'context' => array(
                                        'forceNew' => true,
                                        'module' => 'Home',
                                    ),
                                ),
                            ),
                            'type' => 'simple',
                            'name' => 'dashboard-pane',
                            'span' => 4,
                        ),
                    ),
                    array(
                        'layout' => array(
                            'components' => array(
                                array(
                                    'layout' => 'preview',
                                ),
                            ),
                            'type' => 'simple',
                            'name' => 'preview-pane',
                            'span' => 8,
                        ),
                    ),
                ),
                'type' => 'default',
                'name' => 'sidebar',
                'span' => 12,
            ),
        ),
    ),
    'type' => 'simple',
    'name' => 'base',
    'span' => 12,
);

Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild.