Introduction to SmartClient 8.1/SmartGWT 2.5 Validation (part 3)

October 19, 2011 Comments off

Up to this point of the introduction series you have seen how to apply validators to fields and create custom validators. Now we take a look validation features to control when validation occurs and how errors are shown.

Controlling validation

As has been shown previously validation can be performed on a form by calling myForm.validate() at any time. This is not the only way to trigger validation. A single form item can be validated by calling myFormItem.validate() as well. In both cases the return value is a boolean indicating if the validation was successful.

In addition to manually triggered form validation, a form is always validated before saving. The save is not performed if validation fails. The same process is repeated on the SmartClient Server Framework before a save is completed. Validation failures are reported back to the client and save is aborted.

Client-side validation can be disabled on a form by setting myForm.disableValidation to true. When set no validation is performed on calls to myForm.validate() or saveData(). Server validation is still performed on save. This setting does not affect any field-level validation triggers.

Automatic field-level validation

Form fields can be validated automatically on loss of focus (field exit) or on each value change. To enable validation on change set myFormItem.validateOnChange to true. Validation on the field will occur after the transformInput() handler has been called and before the form item’s change() handler. If validation fails the value change will not be saved into the field value nor will the change() handler be called.

Validating on a change is useful when the value in the field should always be in a valid state. This doesn’t work well for a date because it is invalid by design during partial entry. It should only be valid when leaving the field. Enter validateOnExit. By setting this property to true, the field will be validated just before calling the field’s editorExit() handler.

Normally, field focus moves to the next field even when validation fails when validateOnExit is true. To force the user to remain in the invalid field instead, set myFormItem.stopOnError to true. This implies that myFormItem.synchronousValidation is enabled so server-only validators are processed synchronously. The synchronousValidation setting is covered in a later in Advanced features.

Setting hierarchy

In many cases the FormItem settings can also be made at the DynamicForm level. For example, if all fields with validators should be validated when leaving the field, set myForm.validateOnExit to true. This validation will be performed if either myForm or myFormItem has validateOnExit true.

The validateOnChange setting is slightly different. This setting can be made at the form, field or validator level. If either the form or field validateOnChange is true validation is performed on each value change. Validations are also performed for any validator marked validateOnChange:true. However, any validator explicitly marked with validateOnChange:false is skipped during this validation step no matter what the other settings are. Some validators just don’t apply during the change phase.

Field dependencies

When working with custom validators they likely refer to more than one field on the record. Consider a custom validator that validates that one field is greater than another. If either field is changed the validator on the other field must be re-evaluated. This can be accomplished by providing the list of field dependencies for the validator as dependentFields:

Assume there is a pre-defined validator called “isGreaterThan” that compares the current field value against the current value of another field. The following DataSource will enforce that max is greater than min whenever the record is saved or validated or the max field value is changed. What happens when the min field value is changed? Nothing.

<DataSource ID="reorder">
    <fields>
        <field name="min" title="Min. Qty" type="integer" required="true"/>
        <field name="max" title="Max. Qty" type="integer" required="true">
            <validators>
                <validator type="isGreaterThan" otherField=”min” validateOnChange=”true” />
            </validators>
        </field>
    </fields>
</DataSource>

By adding dependentFields to the min field we can fix this situation:

<DataSource ID="reorder">
    <fields>
        <field name="min" title="Min. Qty" type="integer" required="true"/>
        <field name="max" title="Max. Qty" type="integer" required="true">
            <validators>
                <validator type="isGreaterThan" otherField=”min” validateOnChange=”true”>
                    <dependentFields>
                        <value>min</value>
                    </dependentFields>
                </validator>
            </validators>
        </field>
    </fields>
</DataSource>

Now, whenever the field value of min changes, the validator on max will be fired. This occurs even if validateOnChange is not true on the min field.

There is one other way to provide dependencies: declare a conditional validator. This will be covered in a later section.

Error display styles

SmartClient supports many different error display styles. These settings can even be mixed on a form per form item. By default errors are shown to the left of the field with just an error icon. Hovering the mouse over the mouse displays the full text of the error(s). To accomplish this, SC uses the following DynamicForm default settings:

showErrorIcons : true
showErrorText : false
showErrorStyle: true
showInlineErrors : true
errorOrientation : “left”

A FormItem has many of the same properties to control the display of errors as the DynamicForm. If no override value is provided at the field level, the DynamicForm settings applies. This makes is easy to define the form-wide settings and tweak one or two fields to show differently.

The icon shown can be changed with the following properties: errorIconSrc, errorIconHeight and errorIconWidth. These are normally provided by the selected skin or default from the base FormItem class.

The position of the icon can be changed by setting errorOrientation to “top”, “bottom”, “left” or “right.” This orientation is in relationship to the input field itself unrelated to the field title. If text is shown inline as well (showErrorText: true), it uses the same orientation and follows the error icon if displayed. When showing error text on the left or right, the amount of space taken from the field can be adjusted by setting errorMessageWidth on the specific field. The default is 80 pixels (80) and is limited by the column width.

Fields that are in an error condition can also be styled uniquely. By default a FormItem use CSS style “formCell” (as set in cellStyle). When showErrorStyle is false this same style is applied. Setting showErrorStyle to true causes to SmartClient to apply a CSS style of the value from cellStyle with “Error” appended (“formCellError” by default). This style in the latest skins writes the field title in bold.

If you are showing fields without titles and handling errors with just an icon it might be helpful to include the field title in the error hover message. To do so, set form.showTitlesWithErrorMessages to true. Messages will be placed in the tooltip after the title and a colon (:).

Showing errors as a group

In addition to the style of inline errors just covered, errors can be shown as a group for the form. This is common for a densly packed form where there is not room for error icons or text. To turn off inline errors set form.showInlineErrors to false.

For group error display, a special error cell (field type “blurb”) is created and shown at the top of the form when any field has errors. The field’s cellStyle defaults from form.errorItemCellStyle (“formItemError”) allowing customization of the display.

In this blurb the errors are shown formatted from form.GetErrorsHTML(). That is, each field in error is shown as a bullet item along with the error message(s). A preamble message is typically shown above this list. The text for this message is defined in form.errorsPreamble as defaults to “The following errors were found:”.

Choosing the cell style for the errors group is not the only customization that can be done. The error display is just a form item so any form item properties can be defined on this field by setting them in form.errorItemProperties. This property is an object with the desired properties.

For example, if you want to launch help when anything is clicked on this item, set properties like:

myForm.errorItemProperties : {
    click : “form.launchHelp()”
}

Hidden field errors

Hidden fields can be validated by a call to myForm.validate(true). Note the true argument. This tells the validation process to include hidden fields. Normally it is assumed that hidden fields are managed by code to be valid or are always valid when hidden.

When showing errors inline, there is no where to show an icon or message because the field is hidden. If errors are shown as a group, hidden errors are included. What do you do with hidden errors in inline mode? There is an event handler that allows you to do whatever you want like showing a message box.

handleHiddenValidationErrors is a string method so you can just call your display method using the errors parameter.

handleHiddenValidationErrors : “myForm.showHiddenErrorBox(errors)”;

Non-validator errors

Validators are just one way of setting errors on a field. Validation failures make the equivalent of calls to DF.addFieldErrors to store the error messages. You can do this programmatically as well. Say your form needs to perform some validation on another form or object (a valuesManager is not appropriate). After calling the normal myForm.validate() this additional validation results in errors for one or more fields. These errors can be added to the form’s error list by calling myForm.addFieldErrors. These errors then show exactly like errors that result from validators.

myForm.addFieldErrors(“email”, “Email must be entered when …”, true);

Note the last argument of true. This forces a redraw of the field so the errors show immediately. If you have a number of fields to update you can optimize this process by passing false instead and then calling myForm.showErrors() to redraw fields with errors.

WARNING: Performing form validation or validation of the field updated with your non-validator errors (like with field.validateOnChange) clears your custom message(s).

Finally, errors can be cleared on demand with a call to myForm.clearErrors (all fields) or myForm.clearFieldErrors (one specific field). MyForm.setFieldErrors is a shortcut for calling clearErrors() and then addFieldErrors().

Determine validation state

There are many cases where a form needs to determine the current validation status of its fields such as for enabling or disabling action buttons.

The SmartClient DynamicForm has some helpful methods for just this use:

  • hasErrors()
  • hasFieldErrors()
  • valuesAreValid()
  • getValidatedValues()

The first, hasErrors(), returns true if the form currently has any errors. The second, hasFieldErrors(), is the same except it only checks a single field. For both of these validation must have been performed previously to be useful.

The last two methods actually trigger validation but valuesAreValid() does not save any validation errors on the form. A call to getValidatedValues() is the same as calling getValues() except that validation is performed first and values are only returned if there are no errors.

Tags:

Introduction to SmartClient 8.1/SmartGWT 2.5 Validation (part 2)

October 14, 2011 1 comment

In the last part of the validation introduction the basics were covered. In this installment we will take a look at custom validation.

Note that SmartClient 8.1 and SmartGWT 2.5 were released since the last part so the title was updated but the content still applies to the 8.0 and 2.2+ releases.

Custom Validators

Although SmartClient offers numerous built-in validators that solve many of your standard validation needs, there are always situations where validation is required that are not covered by existing validators. Enter the custom validator.

Custom validators can be created to run on either the server or client. There is currently no mechanism to write a single validator that works on both the client and server. We’ll start with a look a custom validation on the server.

A special word on server-based validators should be added here: Just because a validator is server-based doesn’t mean it cannot be triggered during form/grid editing. In fact, by applying the validator to a DataSource field the client will recognize the validator and ask the server to perform validation just like it does for client-only validators.

Server custom validators

A serverCustom validator can process Velocity expressions or make a DMI call to a server object. These two options provide extensive flexibility to extend the server validation features.

Velocity expressions

The SmartClient Server Framework leverages Apache’s Velocity Engine to allow dynamic expressions to be evaluated for validation (and other server features not covered in this document). Let’s return to the examples to see one of these validators in action. Select Forms → Validation → Server-based → Velocity Expression.

In this example a user is selecting an item and quantity to place on an order. The user should be informed when the desired quantity is not in stock. To do this a serverCustom validator is applied to the DataSource (velocity_orderForm) quantity field. Select the velocity_orderForm tab to see the DataSource definition.

<DataSource ID="velocity_orderForm" serverType="sql">
  <fields>
    <field name="orderItem" type="sequence" primaryKey="true"/>
    <field name="itemId" foreignKey="StockItem.id"/>
    <field name="quantity" type="integer">
      <validators>
        <validator type="serverCustom">
          <serverCondition><![CDATA[
            $value < $dataSources.StockItem.fetchById($record.itemId).quantity
          ]]></serverCondition>
          <errorMessage>Not enough in stock</errorMessage>
        </validator>
      </validators>
    </field>
    <field name="instructions" type="text"/>
  </fields>
</DataSource>

Take a look at the validator for the quantity field. First, note the use of to allow the expression to be entered without conflicting with the XML source. The serverCondition must be a single expression that evaluates to true or false. Next, notice the meaningful Velocity variables in this expression: $value, $dataSources, and $record. These variables are provided automatically by the framework for context. Here are the common context variables available:

dataSources The list of all DataSources accessible by name (i.e. $dataSources.supplyItem)
dataSource The current DataSource
record The DataSource record being validated
value The value of the field
validator The configuration of this validator presented as a Map
field The field as a DSField object
util A com.isomorphic.util.DataTools object giving access to all of that class’s useful helper functions

Breaking down the expression, you can see the lookup of the related DataSource StockItem by $dataSources.StockItem. Any DataSource method can be called on that DataSource or a property can be read. Here the fetchById method is called to lookup a particular record using the primary key $record.itemid. The $record object normally has all fields of the record being validated allowing inter-field validations or references. With the StockItem record now loaded, the quantity column value (quantity in stock) is compared to our desired quantity value. The result is true if enough stock is on hand.

The validator errorMessage is also set to the desired message that the user should see upon validation failure.

If the quantity field is placed on the form as-is, when the form submit is pressed the new record will be validated on the server using this custom validator and errors reported back to the client on failure. This is great but it would be nice for the user to know the quantity is not available before trying to submit the form. To do this, validateOnExit is set true on the quantity form field. Details on this option are covered in the Controlling validation section.

Server objects

Custom server validators based on Velocity are very powerful and quickly created but cannot cover very complicated validations. For these cases the SmartClient Server Framework provides the ability to define server-based validators using DMI on custom objects. Select Forms → Validation → Server-based → DMI Validation.

This example looks identical to the one covered for Velocity validation. The end result is very similar except the error message includes details on how many items are available in stock. The form itself is the same except it refers to a different DataSource. Click on the validationDMI_orderForm tab.

<DataSource ID="validationDMI_orderForm" serverType="sql">
  <fields>
    <field name="orderItem" type="sequence" primaryKey="true"/>
    <field name="itemId" foreignKey="StockItem.id"/>
    <field name="quantity" type="integer">
      <validators>
        <validator type="serverCustom">
          <serverObject lookupStyle="new"
            className="com.isomorphic.examples.server.validation.ValidatorDMI"/>
          <errorMessage>Only $available in stock</errorMessage>
        </validator>
      </validators>
    </field>
    <field name="instructions" type="text"/>
  </fields>
</DataSource>

The serverCustom validator assigned to the quantity field is defined with serverObject. The errorMessage is defined with a variable, $available, instead of static text.

A serverObject definition can be used to reference server objects in many ways. This example refers to the custom class com.isomorphic.examples.server.validation.ValidatorDMI. A new instance will be created for each validation because the lookupStyle is set to “new.” See the SmartClient Reference for additional options.

This example refers to a class that implements exactly one method: condition. A custom validation class can implement more than one condition-type method. The specific method for the validator is then specified by the methodName property for the serverObject.

Click the ValidatorDMI.java tab to see the implementation. In all cases the “condition” method must accept the value, validator, fieldName, record and ds arguments. Note that the example validator method performs the same operations as the Velocity example using the standard SmartClient Server Framework objects and methods. One thing unique is setting a variable on the validator so that the error message can be dynamic. The $available variable is set when validation fails by calling validator.addErrorMessageVariable. All variables must be strings.

It should be obvious just from these two custom validation mechanisms that the client and server validation features of the SmartClient Server Framework are invaluable for robust user experiences and reduce development efforts to just providing the business rules.

Client custom validators

Although not as useful as server-based custom validators, client-only custom validators can be used to provide another layer of validation to forms.
Returning to the Forms->Validation->Custom Binding example, let’s add some additional choices. Above the “acceptTerms” field add the following fields:

        {type:"header", name: "choices", defaultValue:"Choose notification type(s):"},
        {name: "byMail", title: "Mail", type: "boolean"},
        {name: "byPhone", title: "Phone", type: "boolean"},
        {name: "byEMail", title: "E-mail", type: "boolean"},
        {name: "byText", title: "Text message", type: "boolean"},

Here the user is choosing one or more ways to be notified in the future. At this point in time, the user can choose any of these or none. But this is not exactly what we want – to require at least one choice. Let’s add a custom validator to the choice header to notify the user if no choice is made. Change the “choices” header field as follows:

        {type:"header", name: "choices", defaultValue:"Choose notification type(s):", validators: [{
            type: "custom",
            condition: function (item, validator, value, record) {
                 var checkboxes = ['byMail', 'byPhone', 'byEMail', 'byText'];
                 var count = 0;
                 for (var i = 0; i < checkboxes.length; i++) {
                     if (record[checkboxes[i]]) count++;
                 }
                 if (count < 1) {
                     validator.errorMessage = "Must choose at least one notification type";
                     return false;
                 }
                 return true;
            }
         }]
        },

We have now added a simple custom validator to the header that checks each checkbox field for it’s checked status (true) and return a failure if nothing is checked. Note that a validator on a field does not have to perform the validation on the same field. The validator is assigned to the field where the error is reported. This validator could be assigned to the first checkbox field instead avoiding the need for an additional field.

Following this pattern it is very easy to add as many product-specific validations as needed. However, let’s say you want to validate zip codes against a zip code database. Zip codes are used for customer addresses, ship-to addresses, vendor addresses, etc. Copying the same validation code to each form field or even DataSource field is unwieldy. A better choice is to register a custom validator as a built-in. This is covered in the Advanced features section.

In the next part we’ll take a look at options that control when validation occurs and how the results are presented to the user.

Tags:

SmartGWT Custom FormItems

October 29, 2010 Comments off

Building a custom FormItem for use in a DynamicForm is straight forward. However, various common practices prevent the same FormItem from being used in a ListGrid. I’ll present some simple examples and try to explain why they don’t work and show what changes make the items usable in a ListGrid.

First Example

public class BadClearTextItem extends TextItem {
    public BadClearTextItem() {
        FormItemIcon formItemIcon = new FormItemIcon();
        setIcons(formItemIcon);
        addIconClickHandler(new IconClickHandler() {
            @Override
            public void onIconClick(IconClickEvent event) {
                System.out.println("getValue() is " + getValue());
                clearValue();
            }
        });
    }
}

This BadClearTextItem is provides nothing more than a standard TextItem with an icon to clear the current value. Give it a try by dropping into a test form. Now try this same item in a ListGrid with:

ListGridField field = new ListGridField();
// ...additional field configuration here
field.setEditorType(new BadClearTextItem());

You should receive an error: Uncaught exception escaped : com.google.gwt.core.client.JavaScriptException(TypeError): self.clearValue is not a function.

Why? The first thing to know is that the setEditorType() method does not really take and hold an instance of a FormItem but rather grabs the properties from the passed template FormItem. When editing begins a new instance of a FormItem is created using the saved template and that editor is shown to the user. I hear another “Why?” question. One reason is you can have editing enabled for all rows (see setAlwaysShowEditors). Another reason is the same editor can be used for a filtering (see setShowFilterEditor).

So what this means is the call clearValue() implies a reference to the current instance of the TextItem (this) but that instance has not yet been rendered so the underlying JavaScriptObject is not yet a FormItem. The getValue() call did not throw an exception because the SGWT wrapper does not attempt to call the underlying FormItem JavaScriptObject if the item has never been rendered – it returns the local starting value, null, or the value set by setValue(). The value logged for getValue() is never correct because it continues to reference the instance created for the template, not the instance associated with the inline editor.

Let’s now look at a simple fix:

public class GoodClearTextItem extends TextItem {
    public GoodClearTextItem() {
        FormItemIcon formItemIcon = new FormItemIcon();
        setIcons(formItemIcon);

        addIconClickHandler(new IconClickHandler() {
            @Override
            public void onIconClick(IconClickEvent event) {
                FormItem item = event.getItem();
                System.out.println("getValue() is " + item.getValue());

                item.clearValue();
            }
        });
    }
}

Instead of using the implied this reference, I have pulled the item instance from the event. This disconnects the event handler code from the instance where it was created.

LESSON: Be aware that this is not always what you expect this to be.

Another example

Here’s an update to the previous working example that only clears the item on every other click. No, this isn’t realistic but is used to simply represent a common use pattern.

public class BadClearTextItem extends TextItem {
    private int count = 0;

    public BadClearTextItem() {
        FormItemIcon formItemIcon = new FormItemIcon();
        setIcons(formItemIcon);

        addIconClickHandler(new IconClickHandler() {
            @Override
            public void onIconClick(IconClickEvent event) {
                FormItem item = event.getItem();
                System.out.println("count is " + count);

                if (++count % 2 == 0) item.clearValue();
            }
        });
    }
}

Drop this item into your test grid and give it try. At first glance it seems to work fine but try editing different rows and clicking the icon. Now take a look at your console output. Note that the value for count increments on every icon click even though you are editing in different rows. How can this be if SmartGWT creates a new instance of the FormItem for every edit session? The reason is the count property is always referencing the instance created for the setEditorType() call, not the instance created on-the-fly for editing. This private instance variable now acts like a static.

public class GoodClearTextItem extends TextItem {
    public GoodClearTextItem() {
        FormItemIcon formItemIcon = new FormItemIcon();
        setIcons(formItemIcon);
        setAttribute("count", 0);

        addIconClickHandler(new IconClickHandler() {
            @Override
            public void onIconClick(IconClickEvent event) {
                FormItem item = event.getItem();
                int count = formItem.getAttributeAsInt("count");
                System.out.println("count is " + count);

                if (++count % 2 == 0) item.clearValue();
                formItem.setAttribute("count", count);
            }
        });
    }
}

This version of the FormItem now maintains a true instance variable for count. By using the getAttribute…() and setAttribute() methods we are sure to be setting the value on the desired instance in our constructor and in our event handlers.

LESSON: Instance variables are not always instance variables.

I hope this helps make building custom FormItems for use in ListGrids (and FilterBuilder) easier. Best of luck!

Introduction to SmartClient 8.0/SmartGWT 2.2 Validation

August 10, 2010 Comments off

SmartClient supports a very powerful validation process for form and grid editing. One or more validators can be assigned to each field providing a means to inform the user of data entry issues.

Validators are typically assigned to fields in a DataSource but can also be applied to DynamicFormFields. A major benefit of assigning validators to DataSource fields is that the validation occurs on both the client and server when using the SmartClient Server Framework. If you are not using the SC Server Framework you will have to write validation logic on the client and server.

Taking server validation one step further, the SC Server Framework provides built-in server-only validators to enforce uniqueness, enforce related records exist and perform additional, custom operations.

SmartClient and SmartGWT have the same basic validation features so this introduction focuses on the SmartClient side. Following along with the SmartGWT Showcase should be fairly easy as well.

Using the Feature Explorer validation samples let’s take a quick tour of basic validation functionality.

Type Validation

Starting with Forms → Validation → Type, take a look at the DataSource:

<DataSource ID="type">
    <fields>
        <field name="intField" title="Integer" type="integer"/>
    </fields>
</DataSource>

This DataSource defines a single field, intField, as type “integer” with a title to show in a form or as a grid header. Where is the validator? SmartClient automatically assigns a built-in validator, isInteger, to this field based on the type “integer.” This validator just confirms that the field value is a whole number and converts any text value to a number.

The test form that uses this DataSource is simple:

isc.DynamicForm.create({
    ID: "boundForm",
    dataSource: "type"
});</pre>
<pre>isc.Button.create({
    top: 60,
    title: "Validate",
    click: "boundForm.validate()"
});

Any value can be entered into the field on the form and nothing happens until the Validate button is pressed. At that time validation is performed on all form fields – one in our case. If the validator on intField fails the form will, by default, show an error icon and provide an error message as a tooltip. There are other ways to trigger validation that will be covered later.

When assigning a specific type (FieldType) to a DataSourceField, the value is automatically converted as required before validators see it. For example, a field of type integer receives an input value typically as a string so it must be converted into a number value. Any assigned validators can then count of the value being validated as being of the correct type.

Using Built-in Validators

SmartClient provides numerous built-in validators that can be used on your fields. See ValidatorType in the reference for available validators.

Select the example Forms → Validation → Type and look again at the DataSource:

<DataSource ID="builtins">
    <fields>
        <field name="intField" type="integer" title="Integer">
            <validators>
                <validator type="integerRange" min="1" max="20"/>
            </validators>
        </field>
    </fields>
</DataSource>

Based on the details in the previous section it is clear that the field will have the isInteger validator assigned automatically. But note that another validator is attached to this field: integerRange. Additional properties, min and max, are assigned to customize this validator instance with the desired range. Neither value is required. In this case an entered value of 1 to 20 inclusive is valid.

Enter the number 27 and press Validate. Note the error is “Must be no more than 20.” This default message comes from the i18n class property Validator.mustBeLessThan. This message is special in that it can be customized based on the validator min and max properties. These properties are available as ${min} and ${max} within the message: “Must be no more than {$max}.” The validator error messages can be customized globally by changing the i18n defaults or per validator instance.

Add the property errorMessage=”Please enter a value between ${min} and ${max}” to the validator in the DataSource after max=”20” and click Try It. Again enter the value 27 and press Validate. Note the changed error message. Even though by default the validator uses two i18n messages, one for the minimum value and one for the maximum value, only one error message can be specified manually.

Value Transformation

An additional feature that a validator can provide is that of value transformation. The built-in mask validator is an example.

Select the example Forms → Validation → Value Transform and look at the DataSource:

<DataSource ID="valueTransform">
    <fields>
        <field name="phone" title="Phone" type="text">
            <validators>
                <validator type="mask"
                           mask="^\s*(1?)\s*\(?\s*(\d{3})\s*\)?\s*-?\s*(\d{3})\s*-?\s*(\d{4})\s*$"
                           transformTo="$1($2) $3 - $4"/>
            </validators>
        </field>
    </fields>
</DataSource>

The mask validator takes two properties, mask and transformTo, defining how the value is to be validated and transformed. If the value entered into this field does not match the mask regular expression an error is returned. Otherwise, the value is rewritten based on the transformTo expression.

Enter the value 123 and press Validate. Note the error “Invalid value” is returned because the mask was not matched. Try the value 1234567890 and note the field value is transformed into a formatted U.S. phone number as (123) 456 – 7890.

Built-in server-only validators

SmartClient ships with two standalone server-only validators: isUnique and hasRelatedRecord. The isUnique is used to prevent entry of a duplicate value on a field. This is checked against all records in the DataSource. Consider the case where new inventory items are added. The SKU is usually required to be unique but the user can enter any value. An isUnique validator can be assigned to prevent duplicates.

Select the example Forms → Validation → Server-based → Unique Check and look at the DataSource. In particular, note the email field:

<field name="email" type="text" required="true">
    <validators>
        <validator type="isUnique"/>
    </validators>
</field>

Here the DataSource has specified that the email field is required and must be unique. If you have followed along, you will know there are two validators assigned to this field: required and isUnique. See the matching field definition on the form:

{name: "email", required: true, validateOnExit: true},

The required property is redundant since the dataSource specifies required. It can be safely removed. Also note the validateOnExit: true property. This property causes validation of this field when focus leaves the field and the value has changed. In this sample, it lets the user know immediately if a duplicate email is entered without requiring a form validation. More on controlling validation will be covered later.

Assuming a value is entered for the email address a validation request is sent to the server. The SmartClient Server Framework takes the request and validates against the actual database that the value does not already exist. A success or failure response is returned to the client and automatically displayed. This process is seemless so the user is unaware the validation occurred server-side.

The hasRelatedRecord validator is similar. It will validate that the entered value for a field exists in a related DataSource. A validator of this type is not needed if the user must select the related value from a list but there are times when the choices cannot be limited that way. Consider a package tracking form where the user enters the tracking number to be tracked. This type of form does not fit the selection list model. Assigning a hasRelatedRecord validator will provide the user feedback required for an invalid entry.

Select the example Forms → Validation → Server-based → Related Records and look at the complaint DataSource. In particular, note the trackingNumber field:

<field name="trackingNumber" type="integer"    title="Tracking #"       required="true"   >
    <validators>
        <validator type="hasRelatedRecord"
                   relatedDataSource="masterDetail_orderHB"
                   relatedField="trackingNumber"
                   errorMessage="Tracking # does not exist" />
    </validators>
</field>

The hasRelatedRecord validator has a number of additional properties. The main two are relatedDataSource and relatedField specifying the relationship to another table. This can be compared to a SQL foreign key constraint. If the entered value is found in the related table validation is passed. You can see the masterDetail_orderHB DataSource to see where the relationship points.

As with the Unique Check sample, the server-side validation is seemless and avoids the client having to pull the full list of records to perform a local check or issuing a special fetch and processing the result.

There is one more server-only validator included with SmartClient, serverCustom, but it requires custom conditions to be defined. It is covered later.

Multiple Failing Validators

It is possible for two validators to fail for a given field. In this case, each error message is shown in the tooltip. Open the example Forms → Validation → Regular Expression and add another validator to the DataSource along with a more descriptive error message for the regexp validator:

<DataSource ID="regularExpression">
    <fields>
        <field name="email" title="Email" type="text">
            <validators>
                <validator type="lengthRange" min="10" max="64" />
                <validator type="regexp"
                           expression="^([a-zA-Z0-9_.\-+])+@(([a-zA-Z0-9\-])+\.)+[a-zA-Z0-9]{2,4}$"
                           errorMessage="Invalid email address format" />
            </validators>
        </field>
    </fields>
</DataSource>

There are now two validators on the “email” field. Click Try It, enter “abc” into the field and click Validate. The error message should include both validator messages.

Multiple messages can be prevented so that the first error found during validation stops the remaining validators from being processed. Add stopIfFalse=”true” to the lengthRange validator in the DataSource. Click Try It again to repeat your test. Note that only the length message is shown. This is the same method used by type validators discussed earlier to ignore remaining validators on failure.

In the next installment we will write and test custom validators.

Tags:

SmartClient 8.0 Beta

August 3, 2010 4 comments

It’s finally here – the SmartClient 8.0 Beta. This release has been a long time coming but the changes are well worth the wait. Check out the details at the Isomorphic blog.

My development work has taken a bit of a twist as I have been doing some projects with SmartGWT. I will say the environment provided by Eclipse and the forced class management as dictated by Java do guide the development in a very manageable direction.

I still don’t like the debugging environment because you can’t really see the JavaScript object properties and the turn around time for refreshing a page is really slow. It gets even worse if the change is to a class variable or event handler because it forces a server restart.

However, when you can avoid setting JSO attributes and stick with the wrapped libraries, the code only fails because of a logic issue, not a compiler / interpreter issue.

Interesting work :)

SmartGWT 2.0 Release includes masked-editing enhancements

December 14, 2009 Comments off

Sanjiv has just released SmartGWT 2.0 and it includes updated masked-editing features. Here are some additional notes:

  • Masked text fields now support both insert and overwrite modes. Insert mode is the default but overwrite mode can be enabled by setting the maskOverWriteMode property.
  • Selecting a portion of the field to delete or type over now responds correctly.
  • Date, DateTime, Time fields now support masked data entry.

For Date fields, setting useMask to true enables the date mask entry. The mask is automatically defined from the inputFormat (or defaults to MDY). You can change the separator character with setMaskDateSeparator.

Time fields work similarly.

These SmartGWT features are based on core SmartClient functionality that will be available in a future SmartClient release.

SectionStack without +/- icon

November 9, 2009 1 comment

Working on my latest project, I wanted a sectionStack on the left side of the window. Having been exposed to Outlook too long, I didn’t like the +/- icons shown on the section headers. Searching the SmartClient forums didn’t result in a workaround. Setting canCollapse: false will hide the icon but then clicking on the header doesn’t do anything. After many attempts, I settled on the following. Set canCollapse: false on each section and handle the click event to trigger the default header click action that occurs when canCollapse is true.

    canCollapse:false,
    click : function() {
        var layout = this.getLayout();
        if (!layout) return;
        return layout.sectionHeaderClick(this);
    }

Edit: 2009/11/10 – As reported by Sanjiv, there is a new v7.0 section property, showExpandControls, that can be used in place of canCollapse to remove the +/- icon but still allow the section to be expanded or collapsed by a header click. Seeing this would have saved me a great deal of time but I just missed it…

Tags:
Follow

Get every new post delivered to your Inbox.