2.6. Step 6 - Show customer

In the next step we want to show how data is communicated between the forms by implementing a simple "show me customer data" use case.

Let's start by adding a button in customers.ui called 'Show':

We add a dynamic property 'form' to the button which we set to:

customer_show?id={customer.selected}
		

As before 'customer_show' is the name of the form to be loaded when we press the button.

We want the currently selected customer to be accessible in the customer_show.ui form, so we have to pass the parameter 'id' as the value of the currenctly selected row in the 'customer' list widget. This is denoted by '{customer.selected}'.

We also add some signals for the double click on the customer list to click the "Show" button:

Now of course we have to create a new form called customer_show.ui. We choose a form layout and add two fields with labels 'Name:' and 'Address:' and each of them having a QLineEdit widget. The names of the widgets should be 'name' and 'address' in order to match the future read request from the server. Temporarily we also add an 'id' field which shows us the current value of the form parameter 'id' passed by the 'customers':

Finally we also need a button which brings us back to the customer list by simply adding a 'form' action with the value 'customers':

If we start the wolfclient and select a customer and try to press 'Show' we will notice that the button is disabled. This is because the form parameter 'id' cannot be set to the id of the currently selected customer. Widgets in wolfclient have the default behaviour of using the 'id' attribute as an identifier for the whole row. In the previous step we didn't map the 'id' from the XML to the 'id' property of the row. So we change the 'answer' property of the 'customer' widget in the 'customers.ui' form:

CustomerList list {
    customer[] {
        id={row.id};
        name{{row.name}};
        address{{row.address}}
    }}
		

'id={row.id}' maps the 'id' attribute to the 'id' property of each row.

The 'id' is now an XML attribute and not an element anymore. We also would like to do some validation on the output, so we can rely on each customers to actually have an 'id' attribute.

This is the moment we go back to the server and start with output form validation. We add a form called 'CustomerList' to the file Customer.sfrm form which describes the result of the 'CustomerListRequest' more precisely and especially declares the 'id' as mandatory attribute of the customer. The form 'CustomerList' contains now the meta data element definition for 'root' ("-root list"). We will not have to define it anymore in any directmap RESULT directive for 'CustomerList':

FORM CustomerList
    -root list
{
    customer []
    {
        id !@string
        name string
        address string
    }
}

	

We change now the command in tutorial.dmap again to switch on validation. The SKIP attribute is removed and the root element does not have to be specified because it is defined now in the form declaration:

COMMAND CustomerListRequest CALL SelectCustomerList RETURN CustomerList;
	

Checking with:

netcat -v localhost 7661 < CustomerListRequest.netcat
	

or:

wolframec -d CustomerListRequest.xml
	

we get now:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE list SYSTEM "CustomerList">
<list>
    <customer id="1">
        <name>Dr Who</name>
        <address>Blue Police Box</address>
    </customer>
    <customer id="2">
        <name>John Smith</name>
        <address>The Wheel in Space</address>
    </customer>
</list>

	

Now the 'Show' button is no longer disabled and when double-clicking an entry in the list of customers the wolfclient shows the id of the customer.

The 'name' and the 'address' fields are still empty though. We could of course use two form parameters 'name' and 'address' to propagate the values between the two forms, but if the form gets more complex, this is not a good idea. It's better to load the data for one customer, selecting the data by the current 'id'.

We start by setting the 'action' property on the customer_show.ui form as follows: we want it to execute a request with document type 'CustomerRequest' which searches for a single customer by customer id:

The XML sent to the server will look as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE customer SYSTEM 'CustomerRequest'>
<customer id="2"/>

	

We also get in the client:

no command handler for 'CustomerRequest'
	

and in the server:

ERROR: Error calling procedure: no command handler for 'CustomerRequest'	
	

We see, that the request is sent to the server, but we didn't define the necessary things in the server yet. So we add another simple mapping:

COMMAND CustomerRequest CALL SelectCustomer RETURN Customer;
	

to tutorial.dmap.

We define a new form 'CustomerRequest' in Customer.sfrm which contains the validation of the customer request. The customer request should have except the root element 'customer' and a mandatory attribute 'id' to search for:

FORM CustomerRequest
    -root customer
{
    id !@string
}

	

Of course we have to define a transaction function 'SelectCustomer' in Customer.tdl:

TRANSACTION SelectCustomer
BEGIN
    INTO . DO SELECT * from Customer WHERE id=$(id);
END
	

The '$(id)' refers to the 'id' we pass down for the customer record to retrieve. We don't have to specify '$(customer/id)' here as the root element is always removed before.

We also have to define how the result should be mapped, so we add a 'Customer' form to Customer.sfrm:

FORM Customer
    -root customer
{
    id !@string
    name string
    address string
}

	

We can see in the shell if this new request is working. We define create a file called CustomerRequest.xml with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE customer SYSTEM 'CustomerRequest'>
<customer id="2"/>

	

We can then see that our request is working by executing:

wolframec -d CustomerRequest.xml
	

which returns us:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE customer SYSTEM "Customer">
<customer id="2">
    <name>John Smith</name>
    <address>The Wheel in Space</address>
</customer>

	

Now if we restart client and server and we click on the second customer in the list we get:

So we successfully read the data of a customer into a form.

This time we didn't specify an 'answer' attribute in the 'customer_show.ui' form. So how could the data be mapped back into the widget? There is an implicit mapping of elements by name, so the contents of the 'name' XML element are mapped into the widget with name 'name'. This should only be used if the forms are simple, it's better to specify an explicit answer as follows:

Customer customer { id={?}; name{{name}}; address{{address}} }