6.5. FactPanel
FactPanel displays FactItem in a HorizontalPanel as shown in figure.
Figure 6.4. FactPanel
FactItem
is a widget with two Labels, one for text and another for
value, and FactPanel holds the FactItems. Instead of UiBinder template,
FactPanel
and FactItem
directly extends HorizontalPanel
.
in.fins.client.widget/FactItem.java
public class FactItem extends HorizontalPanel {
private Label label;
private String[] matchs;
private Label value;
private String category;
@UiConstructor
public FactItem(String category, String text, String match) {
this.category = category;
this.matchs = match.split("[,]+");
label = new Label(text);
label.setStyleName("fins-KeyValuePanel-Label-Key");
add(label);
value = new Label();
value.setStyleName("fins-KeyValuePanel-Label-Value");
add(value);
}
....
FactItem
uses @UiConstructor annotation for constructor as Snapshot’s
UiBinder requires this to instantiate the FactItem.
Following UI template, in Snapshot.ui.xml
, instantiate FactPanel and
FactItems.
in.fins.client.content/Snapshot.ui.xml
<g:layer left="2%" width="96%" top='6%' height='7%'>
<f:FactPanel>
<f:FactItem category="Quote" text="FV" match="FV" />
<f:FactItem category="Quote" text="BV" match="BV" />
<f:FactItem category="Quote" text="High/Low"
match="52 Wk High,52 Wk Low" />
<f:FactItem category="Quote" text="EPS" match="EPS" />
<f:FactItem category="Quote" text="DIV" match="DIV (%)" />
<f:FactItem category="Quote" text="DY" match="DY (%)" />
<f:FactItem category="Quote" text="MC" match="Mk.Cap" />
<f:FactItem category="Quote" text="Ind. PE" match="Ind. PE" />
</f:FactPanel>
</g:layer>
UiBinder creates FactItems through constructor marked with
@UiConstructor and then add them to FactPanel
as child widgets and
then add FactPanel to layer of LayoutPanel
. This is the first time we
are adding custom widget as the children of another custom widget. This
is allowed only if parent widget, in this case FactPanel, implements
HasWidgets
interface. Then how come UiBinder is able to add FactItem
to FactPanel without implementing HasWidgets and reasons being,
FactPanel
extends HorizontalPanel
which implements HasWidgets. In
case we use UiBinder for FactPanel then we have to implement HasWidgets,
and we will do this for another widget in a later section.
UI entry <f:FactItem category="Quote" text="High/Low" match="52 Wk High,52 Wk Low" />
is bit different which has two keys in match, 52 Wk
High and 52 Wk Low delimited with a comma. This FactItem takes the value
from two Facts and displays them in a single FactItem. Constructor
splits string variable, match, into String
[] and which is used later
to match against Fact with key “52 Wk High” and another Fact with “52 Wk
Low”.
Next we have to pass the selected Symbol
to FactPanel
so that it
displays the fact values.
SymbolEvent and SymbolAction
When user selects a name in AutoSuggest
, it has to fetch the Symbol
from SymbolDatabase
and fire a SymbolEvent
so that any interested
client may retrieve the Symbol from SymbolEvent. Symbol holds data in
DataGroup
, Data
and Fact
, and FactPanel has to iterate over the
received Symbol, and display the Facts.
AutoSuggest is a component of view layer whereas, task of fetching a
Symbol is an aspect of domain/model layer and therefore dealing with
SymbolEvent in AutoSuggest is not a good design practice, and instead a
mediator has to handle this task. SymbolAction
acts as mediator
between AutoSuggest and clients like FactPanel, and it fetches a Symbol
from server, encapsulate it in SymbolEvent and fires the event.
Propagation of events from AutoSuggest to FactPanel happens in two stages as shown in the figure. AutoSuggest fires NameEvent on name selection and SymbolAction handles this event, and once SymbolAction fetches Symbol it fires a SymbolEvent, and FactPanel handles it to display facts.
Figure 6.5. Event Propagation
First instantiate SymbolAction and let it handle NameEvent.
in.fins.client.content/Snapshot.java
public Snapshot() {
....
SymbolAction symbolAction = new SymbolAction(getFilterMap());
symbolAction.setEventSource(this);
EventBus.get().addHandlerToSource(NameEvent.TYPE, this, symbolAction);
}
....
private static Map<String, String[]> getFilterMap() {
Map<String, String[]> filterMap = new HashMap<String, String[]>();
filterMap.put("Quote", getFilter("Quote"));
return filterMap;
}
private static String[] getFilter(String cat) {
if (cat.equals("Quote")) {
return new String[] { "Price", "PB", "P/E", "BV", "DY (%)",
"DIV (%)", "FV", "EPS", "Mk.Cap", "Ind. PE",
"52 Wk High","52 Wk Low" };
}
return null;
}
Snapshot
creates SymbolAction
and adds it as handle for NameEvent
.
Snapshot is set as eventSource for both SymbolAction
and
AutoSuggest
. Static method Snapshot.getFilter()
returns keys and
SymbolDatabase
generate Facts for those keys. Initially getFilter()
returns keys for only one category i.e. Quote, as FactPanel displays
Facts from that category, and later more categories are added. With this
code, SymbolAction receives the NameEvent and fetches Symbol which
contains Facts as specified by filterMap()
.
Next job is to fire SymbolEvent from SymbolAction and let the FactPanel handle the SymbolEvent.
in.fins.client.action/SymbolAction.java
@Override
public void onNameChange(NameEvent nameEvent) {
getSymbol(nameEvent.getName());
}
private void getSymbol(String symbolName) {
Symbol symbol = SymbolDatabase.getSymbol(symbolName, filterMap);
for (String category : SymbolHelper.getCategories(symbol)) {
SymbolHelper.setPositionDate(symbol, category,
SymbolHelper.getLastDate(symbol, category));
}
EventBus.get().fireEventFromSource(
new SymbolEvent(symbol),eventSource);
log.fine("Fired SymbolEvent " + symbol.getName());
}
in.fins.client.widget/FactPanel.java
@Override
public void onSymbolChange(SymbolEvent event) {
setVisible(true);
Symbol symbol = event.getSymbol();
for (int c = 0; c < getWidgetCount(); c++) {
Widget widget = getWidget(c);
if (widget instanceof FactItem) {
FactItem item = (FactItem) widget;
Date date = SymbolHelper.getPositionDate(symbol,
item.getCategory());
List<Fact> facts = SymbolHelper.getFacts(symbol,
item.getCategory(), date);
item.setValue(getValue(facts, item.getMatchs()));
}
}
}
ComplexPanel.getWidget()
returns the children, and if it is the
instance of FactItem then its value is set. SymbolHelper method is used
to get the List<Fact>. Private method getValue()
on match returns
the value from the List<Fact>. FactPanel is hidden with
setVisible(false)
in the constructor and made visible only when
SymbolEvent is received else FactPanel is visible even before user
selects a name.
Finally, make FactPanel as a handler for SymbolEvent
in.fins.client.content/Snapshot.java
@UiFactory
public FactPanel factPanelFactory() {
FactPanel fp = new FactPanel();
EventBus.get().addHandlerToSource(SymbolEvent.TYPE, this, fp);
return fp;
}
FilterMap
SymbolDatabase populates the Symbol with Fact based on keys in FilterMap and this makes the job of minting new Symbols with random values quite easy. Its real purpose is not that instead it reduces the data transmitted over the network. In actual data store, there are more than 200 financial data points for each symbol. In case, SymbolAction fetches the unfiltered Symbol with all these data point then bloated Symbol uses unnecessary bandwidth. Instead, FilterMap is sent to sever to trim down the symbol before transmission to save network resources but more about this in the next part when we cover the RPC calls.
This pattern of event handling is used extensively for the custom widget from now onwards.