6.6. FactDisclosePanel
FactDisclosePanel shows or hide the Price, PB and PE graphs.
Figure 6.6. FactDisclosePanel
FactDisclosePanel
(FDP) uses two GWT widgets, GWT DisclosurePanel
and inner CaptionPanel
. DisclosurePanel
, contains header and a
content panel, that discloses the underlying content when a user clicks
the header, and CaptionPanel
encloses its contents with border and a
caption on upper left corner.
in.fins.client.widget/FactDisclosePanel.java
@UiField(provided = true)
DisclosurePanel dPanel;
@UiField
CaptionPanel cPanel;
@UiConstructor
public FactDisclosePanel(String header, String caption) {
FinsResources images = FinsResources.INSTANCE;
dPanel = new DisclosurePanel(images.barChart(), images.chart(), header);
initWidget(binder.createAndBindUi(this));
cPanel.setCaptionText(caption);
dPanel.setContent(cPanel);
setVisible(false);
}
Instead of stock header, we want custom header for DisclosurePanel
,
and for this we use @UiField(provided = true)
annotation for
disclosurePanel
field and instantiate it before calling UiBinder.
CaptionPanel
is set as content of DisclosurePanel to which child
widgets are added by implementing the HasWidget interface.
Following Ui declaration adds FDP to Snapshot.
in.fins.client.content/Snapshot.ui.xml
<g:row>
<g:customCell>
<f:FactDisclosePanel header="Price" caption="Price" category="Quote">
<f:ChartPanel title="Price" key="Price" width="450px"
height="220px" rangeSelector="false" />
</f:FactDisclosePanel>
</g:customCell>
<!-- likewise for PE and PB -->
</g:row>
This UI declaration adds FDP to Snapshot and a ChartPanel to FDP and ChartPanel uses attributes width, rangeSelector etc to customize the chart.
HasWidgets
To enable custom widget to hold other widgets, we have to implement
HasWidget
interface, and FDP has to hold ChartPanel and for this it
implements HasWidget
. This interface enables the widget to add or
remove child widgets and to enumerate the widgets it contains. It
defines following methods.
add(Widget w)
- adds a child widget.clear()
- removes all child widgets.iterator()
- returns iterator for the contained widgets.remove(Widget w)
- removes a child widget.
in.fins.client.widget/FactDisclosePanel.java
@Override
public void add(Widget w) {
cPanel.add(w);
// additional logic explained later
}
@Override
public void clear() {
cPanel.clear();
}
@Override
public Iterator<Widget> iterator() {
return cPanel.iterator();
}
@Override
public boolean remove(Widget w) {
return cPanel.remove(w);
}
In these methods, we add (as well as remove and clear) ChartPanel widget to CaptionPanel.
DataGroupEvent and DataGroupAction
SymbolEvent
that was used earlier provides data typically for a date
for the entire symbol, but to plot the graph we require data for all
dates between two periods for specific Fact. For example, to plot Price
graph we require Facts which has key Price for a range of dates.
DataGroupEvent
holds a DataGroup for a specific category with data for
all dates. For price graph, it holds DataGroup
for Quote category, and
from DataGroup we can get List<Data>
and each Data contains
List<Fact> which has Price Fact.
DataGroupAction
fetches DataGroup
of a category and fires
DataGroupEvent
. Like SymbolAction, which was covered earlier,
DataGroupAction is a mediator between two widgets and is responsible to
fetch DataGroup from datastore.
Flow of events
Flow of event is as shown in next figure.
Figure 6.7. Flow of Events
Event flow happens in two stages. Initially, when user selects a name,
SymbolAction
fires a SymbolEvent
which is handled by
DataGroupAction
and assigns this symbol to a field for later use. This
event is common for the entire Snapshot page, and consumed by other
actions as well. Later when user expands FDP, it generates an
OpenEvent
and DataGroupAction
handles this event to fetch
DataGroup
for the symbol held by its field and fires DataGroupEvent
.
This event is propagated to FDP and then to its child widget
ChartPanel
which uses DataGroup’s Data and Facts to plot the graph.
DataGroupAction fetches DataGroup when user expands the FDP, and with
the help of boolean flag, symbolChanged
, reuses the DataGroup on
subsequent clicks until user selects another symbol.
in.fins.client.action/DataGroupAction.java
private void execute() {
if (symbolChanged == false && dataGroup != null) {
log.fine("No Symbol change. Using existing dataGroup");
EventBus.get().fireEventFromSource(new DataGroupEvent(dataGroup),
eventSource);
} else {
DataGroup newDataGroup = SymbolDatabase.getDataGroup(
symbol.getName(), category, filter);
log.fine("Symbol Changed. Fetching dataGroup");
....
Wiring all of them
UiFactory of FDP and ChartPanel wire them with DataGroupAction
.
in.fins.client.content/Snapshot.java
@UiFactory
public FactDisclosePanel factDisclosePanelFactory(String header,
String caption, String category) {
DataGroupAction<DisclosurePanel> dataGroupAction =
new DataGroupAction<DisclosurePanel>(
category, getFilter(category));
EventBus.get().addHandlerToSource(SymbolEvent.TYPE, this,
dataGroupAction);
FactDisclosePanel fdp = new FactDisclosePanel(header, caption);
// required to make fdp visible after symbol is loaded
EventBus.get().addHandlerToSource(SymbolEvent.TYPE, this, fdp);
fdp.addOpenHandler(dataGroupAction);
EventBus.get().addHandlerToSource(DataGroupEvent.TYPE, dataGroupAction,
fdp);
return fdp;
}
@UiFactory
public ChartPanel chartPanelFactory(String title, String key, String width,
String height, boolean rangeSelector) {
ChartPanel cp = new ChartPanel(title, key, width, height, rangeSelector);
return cp;
}
FDP also listens to SymbolEvent
make itself visible on name selection.
This makes FDP visible only after symbol is loaded whereas use of
NameEvent
makes visible slightly before, which may look odd. Other
widgets of Snapshot share the SymbolEvent and hence its use doesn’t
affect the performance.
It is essential to understand that, FDP attaches open handler to
DataGroupAction
and in turn listens for DataGroupEvent
fired by
DataGroupAction.
In factory method, we are able to make FDP as a handler for the DataGroupEvent fired by the DataGroupAction, but FDP has to send it down to ChartPanel and following code accomplishes this.
in.fins.client.widget/FactDisclosePanel.java
@Override
public void add(Widget w) {
cPanel.add(w);
if (w instanceof DataGroupHandler) {
EventBus.get().addHandlerToSource(DataGroupEvent.TYPE, this,
(DataGroupHandler) w);
}
}
In FDP, add()
method registers all child widgets, which are instances
of DataGroupHandler
, as handlers of DataGroupEvent
. Once FDP
receives DataGroupEvent, it propagates the event to child widgets. To do
so, it creates a new DataGroupEvent because it is not a good idea to
handle the existing event outside this method.
Event boundary
Never refer the event received from EventBus outside its handler.
Figure 6.8. DataGroupEvent propagation
We will cover Google Chart Tools in a later chapter, and for the moment
ChartPanel
uses a basic VerticalPanel
to display the chart data in
textual format. In handler method, we traverse through List<Data> of
DataGroup and add Facts with matching keys to VerticalPanel as HTML
widget.
in.fins.client.widget/ChartPanel.java
@Override
public void onDataGroupChange(DataGroupEvent dataGroupEvent) {
vPanel.clear();
dataGroup = dataGroupEvent.getDataGroup();
List<Data> dataList = dataGroup.getDataList();
vPanel.add(new HTML(dataGroup.getCategory() + " : " + key));
for (Data data : dataList) {
for (Fact fact : data.getFacts()) {
if (fact.getKey().equals(key)) {
vPanel.add(new HTML(data.getDate() + " - "
+ fact.getValue()));
}
}
}
}
SymbolEvent vs DataGroupEvent
FactPanel designed in the previous chapter could have used DataGroupEvent. But it uses SymbolEvent by taking datasource access and RPC calls into consideration. On name selection, widgets in Snapshot display data from multiple DataGroup in one go, and if each of these widgets use DataGroupEvent, then it involves multiple request to the server. With SymbolEvent, we will be able to get data for multiple DataGroups in single RPC.
As we progress, widgets are getting more and more sophisticated and next in line is FactTable which uses widgets from Cell Widgets group.