Tutorial - Signals and Slots
Author Keith Wong, keithwong@optushome.com.au
Last updated: 19th Jan 2002
Example Code
These are the files for this example.
customer.jsp [source code]
CustomerForm.java [source code]
CustomerFormDesign.java [source code]
PostCode.java [source code]
The binaries can be downloaded below (packaged in a war file). In Tomcat, simple place the war file under
the "webapps" directory, it will automatically explode when Tomcat is started up.
Make sure the objectivehtml-java-alphaX.jar is in the Tomcat $TOMCAT_HOME/lib directory.
You should then be able to view the
form using the URL http://localhost:8080/objectivehtml-oms/customer.jsp. Replace the server address
and port number if your computer is setup differently.
objectivehtml-oms.war
Introduction
This tutorial continues on from the design tutorial. If you have not gone through that tutorial then it is suggested you go back and read that one before proceeding.
In this tutorial we'll give you a quick introduction into how you can use the signals and slots system of OHtml. There are two signals that we will connect to, the first is the "clicked()" signal that is emitted by the HtmlPushButton object and the second is the "valueChanged(String,String)" signal that is emitted by the HtmlTextBox object.
Once again we have our customer details form. This time we'll add some new requirements to the form. To help users fill out the form, we want to automatically populate the Suburb and State field whenever a recognised postcode is entered.
Code Walk-Through
PostCode.java
public class PostCode { private String m_sPostCode; private String m_sSuburb; private String m_sState; ... }
This class represents a postcode. The class contains the suburb and the state associated with the postcode. We will use this class to hold the postcode data.
CustomerFormDesign.java
public class CustomerFormDesign extends HtmlForm { ... public HtmlTextBox m_txtState; ... public CustomerFormDesign() throws Exception { ... // set the name of the form setName("customerform"); ... // table cell 6,0 m_tblLayout.getTableCell(6, 0).setText("Post Code"); // table cell 6,1 m_txtPostCode = new HtmlTextBox(m_tblLayout.getTableCell(6, 1), "m_txtPostCode"); m_txtPostCode.setOnChange("document.customerform.submit();"); // table cell 7,0 m_tblLayout.getTableCell(7, 0).setText("Suburb"); // table cell 7,1 m_txtSuburb = new HtmlTextBox(m_tblLayout.getTableCell(7, 1), "m_txtSuburb"); // table cell 8,0 m_tblLayout.getTableCell(8, 0).setText("State"); // table cell 8,1 m_txtState = new HtmlTextBox(m_tblLayout.getTableCell(8, 1), "m_txtState"); ... } // end constructor }
There are a number of things that have been changed in the form design. This is what has changed:
CustomerForm.java
private HashMap m_hmPostCodes = new HashMap();
This HashMap object will be used to store details of the recognised postcodes for the customer form.
public CustomerForm() throws Exception { ... // lets connect all our signals and slots try { connect(slot("saveForm()"), m_btnSave.signal("clicked()")); connect(slot("updatePostCodeFields(String)"), m_txtPostCode.signal("valueChanged(String,String)")); } // end try to connect catch (InvalidSlotException e) { e.printStackTrace(); // shouldn't happen } // end catch
Here we connect some slots to some signals. The first signal is the "clicked()" signal of the m_btnSave object that will be emitted whenever the button is clicked. The slot method that it is being connected to is the "saveForm()" slot of the CustomerForm object. The second signal is the "valueChanged(String,String)" signal of the m_txtPostCode object that will be emitted whenever the text field is changed. It is connected to the slot method "updatePostCodeFields(String)" of the CustomerForm object. Note that the slot method only takes in one String argument, it is only interested in the new postcode value not the old postcode value. At connection time the argument types of the slot method and signal method are matched, if the types are not compatible then an exception is thrown.
// lets set the update order m_txtPostCode.setUpdateOrder(10); // 10 provides us with a buffer for later on m_btnSave.setUpdateOrder(20); // 20 provides us with a buffer for later on
We now have the condition that if the postcode is changed then we also attempt to fill in the suburb and state fields. If we used the natural update order then what would happen would be whenever the postcode field is changed, the "valueChanged(String,String)" will be fired off and then the updatePostCodeFields(String) method would be executed. The method would populate the suburb and state fields with the appropriate value. But because the suburb and state fields update order comes after the postcode field these fields would be next to be updated with the submitted data, hence the values that were set by the updatePostCodeFields(String) would be overwritten with the submitted data. To fix this problem we set the update order of the postcode field to be after the suburb and state field. So when the "valueChanged(String,String)" signal is emitted now the suburb and state fields have already been updated. We also change the update order of the save button. We want the saveForm() method to only be executed when all the fields have been updated with the submitted data.
// create all the postcodes m_hmPostCodes.put("2000", new PostCode("2000", "SYDNEY", "NEW SOUTH WALES")); m_hmPostCodes.put("3000", new PostCode("3000", "MELBOURNE", "VICTORIA")); m_hmPostCodes.put("4000", new PostCode("4000", "BRISBANE", "QUEENSLAND")); m_hmPostCodes.put("5000", new PostCode("5000", "ADELAIDE", "SOUTH AUSTRALIA")); m_hmPostCodes.put("6000", new PostCode("6000", "PERTH", "WESTERN AUSTRALIA")); m_hmPostCodes.put("7000", new PostCode("7000", "HOBART", "TASMANIA")); m_hmPostCodes.put("0800", new PostCode("0800", "DARWIN", "NORTHERN TERRITORY")); m_hmPostCodes.put("2600", new PostCode("2600", "CANBERRA", "AUST. CAPITAL TERRITORY"));
We put some postcodes into the HashMap cache. For our example the only recognised postcodes are 2000, 3000, 4000, 5000, 6000, 7000, 0800 and 2600.
public void updatePostCodeFields(String sNewPostCode) { // lets try to set the default for the other fields PostCode objPostCode = (PostCode)m_hmPostCodes.get(sNewPostCode); if (objPostCode != null) { m_txtSuburb.setText(objPostCode.getSuburb()); m_txtState.setText(objPostCode.getState()); } // end if postcode exists } // end updatePostCodeFields
This method will update the the state and suburb fields given the new postcode. Notice how the slot method does not know anything about the signal that we have connected to it. This gives us the ability to easily change the slot method to connect to a different signal in the future if the form logic was changed.
customer.jsp
// retrieve the previous form object CustomerForm objForm = (CustomerForm)request.getSession().getAttribute("customerform"); if (objForm == null) { // we need to create the form first... // this can occur if the page is cached // create a new form objForm = new CustomerForm(); // our self defined form // store the new form in the session so we can use it later request.getSession().setAttribute("customerform", objForm); } // end if null // this method magically updates all form data objForm.updateData(request); // this method prints out the current state of the form out to the client objForm.printHtml(out);
Since we are now using the signals and slots system for our event handling we can remove all the event handling code from our jsp page. The jsp now simply creates a customer form if none exists and sets it in the session object.
Conclusion
And thats its, your own you way to using signals and slots in OHtml. Though this tutorial was quite brief, I hope it gives you a good idea on how you can use signals and slots in your applications to help you write better and more manageable code. If anyone picks up mistakes in this tutorial please let me know.