Section 9.4. Building the Bike Editor Forms

team bbl


9.4. Building the Bike Editor Forms

So far, the application looks good, but it doesn't do too much that's useful. You still need to create a BikeForm to display in a titled dialog, allowing the editing of properties on an existing bike. The same form will need to be used to add a new bike to store inventory.

In general the forms framework, with the data binding and validation framework, are three of the fundamental strengths of the Spring Rich Client Project.

9.4.1. How do I do that?

To create your bike form, you'll first define the declarative validation rules for the Bike class. These rules will be defined independent of any presentation code, so they can easily be reused in different environments.

After that, you'll see how to implement the BikeForm itself, and how the framework auto-binds form controls to backing domain object properties for you, with as-you-type validation.

Finally, you'll review the BikeForm integration with the PropertiesCommandExecutor defined in the previous lab (for editing an existing Bike), and see how to reuse the BikeForm in a NewBikeWizard in order to add new bikes to store inventory.

9.4.1.1 Validation

When a user edits or adds a bike, you want the framework to validate any new data. Furthermore, you want any valida tion errors to be clearly communicated to the user. Lastly, you want the framework to prevent forms that contain validation errors from being submitted.

To hook in validation rules, add the following bean to your root application context:

<beans>     <bean            /> </beans>

The above definition selects an implementation of the RulesSource interface that defines validation rules for the domain entities of your RentABike application. The implementation is surprisingly simple:

com.springbook.richclient.RentABikeValidationRulesSource public class RentABikeValidationRulesSource extends DefaultRulesSource {     public ValidationRulesSource( ) {         addRules(createBikeRules( ));     }     private Rules createBikeRules( ) {         return new Rules(Bike.class) {             protected void initRules( ) {                 addRequired("model", maxLength(25));                 add("weight", gt(0.0));                 add("frame", range(0, 100));                 addRequired("serialNo", regexp("[0-9a-fA-f]*"));             }         };     } }

The createBikeRules( ) factory method creates the validation rules for your Bike domain object. The initRules( ) callback adds the following constraints on a Bike's properties:

  • The model property is required and has a max length of 25.

  • The weight property must be greater than 0.0.

  • The frame property must be in the range 0 to 100, inclusive.

  • The serialNo property is required and must match the provided regular expression.

You now need to implement the form that will be used to add and edit bikes, of course. This form will enforce the above validation rules, and will only be enabled for submit when there are no validation errors:

public class BikeForm extends AbstractForm {     public BikeForm(Bike bike) {         super(bike);     }     public BikeForm(FormModel bikeFormModel) {         super(bikeFormModel);     }     protected JComponent createFormControl( ) {         TableFormBuilder formBuilder = new TableFormBuilder(getFormModel( ));         formBuilder.add("manufacturer");         formBuilder.row( );         formBuilder.add("model");         formBuilder.row( );         formBuilder.add("frame");         formBuilder.row( );         formBuilder.add("serialNo");         formBuilder.row( );         formBuilder.add("weight");         formBuilder.row( );         formBuilder.add("status",            getFormModel( ).createBoundComboBox("status", Bike.STATUSES));         return formBuilder.getForm( );     } }

Pretty simple, huh? I can't imagine building form creation being much simpler than this! The TableFormBuilder above automatically selects, creates, and binds editing controls for each of the Bike's domain object properties. These include the manufacturer, model, frame, serial number, weight, and status. The builder also produces properly aligned labels with mnemonics for each form field. Finally, the form provides as-you-type-validation for each field, enforcing the declarative validation rules you just configured!

Now let's revisit your PropertiesCommandExecutor implementation to see again how the BikeForm displays the properties of a selected Bike in the BikeNavigator view:

    private class PropertiesCommandExecutor     extends AbstractActionCommandExecutor {         private BikeForm bikeForm;         public PropertiesCommandExecutor( ) {             // The command should only be enabled when a single object is             // selected in the Bike Tree.             addGuard(new SingleSelectionGuard(bikesTree));         }         public void execute( ) {             // Create a new Bike Form for editing the selected Bike         bikeForm = new BikeForm(getSelectedBike( ));


Note: The Bike Form will let us edit the selected Bike's properties when the Properties command is executed.
            // Display the form in a Titled Dialog             TitledPageApplicationDialog dialog =                 new TitledPageApplicationDialog(bikeForm, getWindow( )) {                 protected boolean onFinish( ) {                     bikeForm.commit( );                     getRentABike( ).saveBike((Bike)bikeForm.getFormObject( ));


Note: Save or update the bike in inventory, using the RentABike façade. Notify the Bike Tree that the selected node may have changed.
                    bikesTreeModel.nodeChanged(getSelectedTreeNode( ));                     return true;                 }             };             // Display the dialog on the screen             dialog.showDialog( );         }     } }

You now have the form fully integrated with the PropertiesCommandExecutor of the BikeNavigator view. When Properties is invoked on a selected Bike in the navigator tree, the BikeForm will be displayed, as in Figure 9-3.

Figure 9-3. BikeForm


Now you'll want this same Form to be reused for creating new Bikes. You'll want to go back to Lab 2, where you created a New Menu command group displayed in the File menu and on the Toolbar. This New Menu group contained a single action command placeholder for the New Bike command. You're now ready to replace that placeholder with a real command instance, which will be a TargetableActionCommand pointing to a "New Bike Wizard."

Recall:

  com.springbook.richclient.window-command-bars.xml      <bean      >     <property name="members">       <list>           <value>placeholder:newBikeCommand</value>


Note: You'll add a placeholder for the New Bike command, because you haven't implemented it yet.
      </list>     </property>   </bean>

You'll replace the placeholder with:

  com.springbook.richclient.window-command-bars.xml   <bean      >     <property name="members">       <list>           <ref bean="newBikeCommand"/>


Note: Point to the "real" New Bike command.
      </list>     </property>   </bean>   <bean     >     <property name="commandExecutor"/>         <ref bean="newBikeWizard"/>


Note: Target the command at the New Bike Wizard. The wizard acts the ActionCommandExecutor for this command.
    </property>   </bean>

And of course, you can't forget the definition of the actual NewBikeWizard itself. Note the wizard should be defined in richclient-definitions.xml because it is a singleton service shared by all windows:

    com.springbook.richclient.richclient-definitions.xml     <bean        >          <property name="rentABike">            <ref bean="rentABike"/>          </property>     </bean>

The NewBikeWizard provides the workflow required to add a new Bike to inventory. On finish (submit), this wizard uses the rentABike to save the new Bike to the database.


Note: The wizard dialog that realizes the Wizard UI.

So what does the wizard work? Take a look at the code for the NewBikeWizard in Example 9-7. By now, the consistency and simplicity of the programming model should really stand out.


Note: The form model for the wizard uses a compound form to edit a single domain object across multiple pages.
Example 9-7. The wizard took less than 25 lines of code, plus an additional few lines for validation
public class NewBikeWizard extends AbstractWizard implements ActionCommandExecutor {     private WizardDialog wizardDialog;     private FormModel formModel;     private RentABike rentABike;     public NewBikeWizard( ) {         super("newBikeWizard");     }     public void setRentABike(RentABike rentABike) {          this.rentABike = rentABike;     }     public void addPages( ) {         addForm(new BikeForm(formModel));     }     public void execute( ) {         if (wizardDialog == null) {             wizardDialog = new WizardDialog(this);             formModel = SwingFormModel.createFormModel(new Bike( ));         } else {             // reset the form to a new, "fresh" Bike instance             formModel.reset( );         }         wizardDialog.showDialog( );     }     protected boolean onFinish( ) {         Bike newBike = (Bike)getNewBike( );         getRentABike( ).saveBike(newBike);         return true;     }     private Bike getNewBike( ) {         formModel.commit( );         return (Bike)formModel.getFormObject( );     } }

9.4.2. What just happened?

You just built a BikeForm for editing the properties of existing Bikes, as well as new Bikes that have yet to be added to store inventory. You learned how to define validation rules enforced by the Form during the editing process, and how to launch the Form for display in command (controller) execution logic. Finally, you gained a brief insight into Spring Rich's wizard framework.

Now if you access the File New Bike command from the Menu Bar, you'll see Figure 9-4. Select the Bike menu item, and the newBikeCommand invokes the execute( ) method on the NewBikeWizard, an ActionCommandExecutor. A WizardDialog then pops up displaying a BikeForm for editing a new Bike domain object, as in Figure 9-5. If the user selects Finish, edits are committed to the backing form object and the new Bike is saved to the database. If the user chooses to Cancel, nothing happens.

Figure 9-4. New bike


Figure 9-5. Create new bike


When a property has a validation error, the Finish button is disabled and an visual indicator is shown along with a descriptive error message. Once all validation constraints are satisfied, the validation error is removed and Finish becomes enabled.

9.4.3. What about...

...other Spring frameworks? In this book, we've tried to give you a taste of the most exciting open source project to come along since Hibernate. It's not a complete picture, though. Spring also integrates workflow, management through JMX, dynamic languages with Groovy, richer AOP through AspectJ, and many other frameworks. More projects come monthly.

But you do have all of the tools at your disposal to know how to integrate them. You've seen how to structure a typical web application. You've learned to attach services to your business logic, adopt a new GUI framework, switch persistence strategies, and write a test with a mock, stub, or POJO. Now, we urge you to stay with Spring through one full development cycle. By doing development, testing, deployment and maintenance with Spring, you'll see just how much of a difference that it can make for you.

    team bbl



    Spring. A developer's Notebook
    Spring: A Developers Notebook
    ISBN: 0596009100
    EAN: 2147483647
    Year: 2005
    Pages: 90

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net