Tuesday, 15 December 2009

Master-Detail on One Page

With all Seam examples of CRUD for Master-Detail relationships that I've seen so far, we always rely on Modal Panels to edit the Detail entities. (My own example posts are here, here, here, and here) However the Modal Panel approach has it's own limitations, mostly around navigation when the user starts to create a new Detail and then changes their mind (as they do).

In any case, popup modal panels tend to disrupt the flow of the page. What would be good is to be able to edit both Master and Detail entities on a single page. The following example produces a screen that looks like:


So to the code. This uses exactly the same backing bean as the other examples - a conversation-scoped edit manager - with no changes at all. Code for that is available here.

The page.xml file looks like:

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd"
no-conversation-view-id="/MasterList.xhtml"
login-required="true">

<begin-conversation join="true" flush-mode="MANUAL"/>

<action execute="#{masterEditor.wire}" on-postback="false"/>

<param name="masterFrom"/>
<param name="masterId" value="#{masterEditor.masterId}"/>


<navigation from-action="#{masterEditor.persist}">
<rule>
<end-conversation/>
<redirect view-id="/OnePageEdit.xhtml"/>
</rule>
</navigation>

<navigation from-action="#{masterEditor.update}">
<rule>
<end-conversation/>
<redirect view-id="/OnePageEdit.xhtml"/>
</rule>
</navigation>

<navigation from-action="#{masterEditor.remove}">
<rule>
<end-conversation/>
<redirect view-id="/MasterList.xhtml"/>
</rule>
</navigation>

</page>


The XHTML file is:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich" template="layout/template.xhtml">

<ui:define name="body">

<h:form id="master" styleClass="edit">

<rich:panel>
<f:facet name="header">Master Details</f:facet>

<s:decorate id="masterDescriptionField" template="layout/edit.xhtml">
<ui:define name="label">Master description</ui:define>
<h:inputText id="masterDescription" size="100" maxlength="100"
value="#{masterEditor.instance.masterDescription}">
<a:support event="onblur" reRender="masterDescriptionField"
bypassUpdates="true" ajaxSingle="true" />
</h:inputText>
</s:decorate>

<div style="clear: both">
<span class="required">*</span>
required fields</div>

</rich:panel>

<a:region>

<h:panelGrid id="detailEdit" columns="2" columnClasses="detailList,detailDetail" width="100%">

<h:panelGroup>

<rich:dataTable id="detailListTable" var="_detail" value="#{detailListFromMaster}"
width="200px" styleClass="scriptPagesList"
onRowMouseOver="this.style.backgroundColor='#EAEAEA';this.style.cursor='pointer'"
onRowMouseOut="this.style.backgroundColor='#{a4jSkin.tableBackgroundColor}'">
<h:column>
<f:facet name="header">
<ui:include src="layout/sort.xhtml">
<ui:param name="propertyLabel" value="Id" />
</ui:include>
</f:facet>
<h:outputText value="#{_detail.id}" />
</h:column>
<a:support event="onRowClick"
reRender="detailEditDetail"
action="#{masterEditor.select()}">
</a:support>
</rich:dataTable>

<div style="clear: both" />

<div class="actionButtons">
<a:commandButton id="NewDetailButton"
value="New Detail"
action="#{masterEditor.createNewDetail}"
reRender="detailEdit"
styleClass="buttonNoBorders">
<s:conversationId />
</a:commandButton>
<a:commandButton id="DeleteDetailButton"
value="Delete Detail"
action="#{masterEditor.removeDetail(currentDetail)}"
enabled="#{currentlySelectedDetail!=null}"
reRender="detailEdit"
styleClass="buttonNoBorders">
<s:conversationId />
</a:commandButton>
</div>

</h:panelGroup>

<h:panelGroup>
<rich:panel id="detailEditDetail" styleClass="panelNoBorder">
<f:facet name="header">Detail Details</f:facet>

<s:div rendered="#{currentlySelectedDetail!=null}">

<s:decorate id="detailDescriptionField" template="layout/edit.xhtml">
<ui:define name="label">Detail description</ui:define>
<h:inputText id="detailDescription"
required="true"
size="100"
maxlength="100"
value="#{currentlySelectedDetail.detailDescription}">
<a:support event="onblur" reRender="detailDescriptionField" bypassUpdates="true" ajaxSingle="true" />
</h:inputText>
</s:decorate>

</s:div>
</rich:panel>
</h:panelGroup>

</h:panelGrid>

</a:region>

<rich:panel styleClass="panelTopBorderOnly"
bodyClass="panelTopBorderOnlyBodyClass">
<div class="actionButtons">
<h:commandButton id="save"
value="Save" action="#{masterEditor.persist}"
disabled="#{!masterEditor.wired}"
rendered="#{!masterEditor.managed}" />

<h:commandButton id="update"
value="Save" action="#{masterEditor.update}"
rendered="#{masterEditor.managed}" />

<h:commandButton id="delete"
value="Delete"
action="#{masterEditor.remove}"
immediate="true"
rendered="#{masterEditor.managed}" />

<s:button id="cancelEdit"
value="Cancel"
propagation="end"
view="/MasterList.xhtml" />
</div>

<div style="clear: both">
<span class="required">*</span>
required fields
</div>

</rich:panel>

</h:form>

</ui:define>

</ui:composition>



You'll note a number of CSS classes which modify the richfaces appearance:

.panelTopBorderOnly {
margin-top:10px;
border-left:none;
border-right:none;
border-bottom:none;
}
.panelTopBorderOnlyBodyClass {
padding-top:0px;
}

.detailList {
vertical-align:top;
width:200px;
height:200px;
overflow:scroll;
}

.detailDetail {
vertical-align:top;
}

input.buttonNoBorders {
margin:1px 1px 1px 0;
padding:0px;
}


Two key points:
  • The ajax region was required to make the insert and delete of the Detail entities work when dealing with a freshly created Master entity.
  • Note the buttons - these are done differently to the seam-gen buttons, because other the creation of a new Detail lost changes to the Master entity - if any.
Really interested to see what people think.

Friday, 11 December 2009

When the cloud is not really a cloud?

In reading this article on Information Week about the Amazon EC2 North Virginia outage, the first thing that came to mind is disappointment. Disappointment that the cloud turns out to be just another data centre. We have quickly come to expect that we should be insulated from underlying hardware issues. Putting software in the cloud should mean that it continues to operate, regardless of an issue with one piece of hardware. Redundancy and failover should be built into the system.

Apparently not. I guess this is a lesson for application providers. We cannot rely on the IAAS vendors, and will need to look at geography as part of the underlying risk assessment process. Perhaps most people already do this, and it's just me that had this expectation?

Tuesday, 10 November 2009

Pay as you go Insurance

I recently read an interesting article by Stephen Dubner and Steven Levitt (of Freakonomics fame), published on the NY Times website. The article discusses a Pay As You Drive (PAYD) automobile insurance product offered by Progressive Insurance in Ohio (US). It's an interesting discussion bridging technology and product design, with the economics thrown in for good measure. The article is a little old (2008), but is well worth a read.

On the Life Insurance side, I recall many, many conversations with the Product guys, hearing of their desire for better information about consumers and the ability to price products accordingly. Of course there are often regulatory constraints to deal with...