DisplayFields
DisplayFields is a templating API that enables integration adapters to provide nested sets of dynamic, internationalised, customisable fields (label-value pairs) to Caplin’s front end applications for display.
DisplayFields are used to display information in:
-
Trade confirmations
-
Blotter summaries
-
Post-trade history
DisplayFields in context
DisplayFields are sent to the front end in the DisplayFields
field of some trade model messages and blotter records.
Message builders that support DisplayFields auto-generate a default value for the DisplayFields
field that you are free to override.
See below a simplified example of DisplayFields being sent to the front end as part of an RFS spot trade confirmation trade model message using the TicketSpotTradeConfirmation
message builder.
public class TradeConfirmationProvider implements CachedMessageProvider<TradeSubjectInfo> {
private SubjectMessagePublisher<TradeSubjectInfo> publisher;
@Override
public void initialise(final SubjectMessagePublisher<TradeSubjectInfo> publisher) {
this.publisher = publisher;
}
@Override
public void onRequest(TradeSubjectInfo subject) {
TicketSpotTradeConfirmation tradeConfirmation =
TicketSpotTradeConfirmation.newBuilder()
.setDisplayFields(
new DisplayFields()
.displayFields(List.of(
new DisplayField()
.label("Account")
.template("{{account}}")
.arguments(Map.ofEntries(
Map.entry("account", new DisplayFieldArgument("User 1", DisplayFieldArgument.TypeEnum.TEXT))
))
))
)
.build();
publisher.publishMessage(subject, tradeConfirmation);
}
}
Anatomy of a DisplayField
Each DisplayField is made up of a label, a template, and a set of arguments. The front end combines these attributes to render a label-value pair. Common combinations can be created using the DefaultDisplayFields
helpers:
Labels, templates, and arguments
Labels, templates, and arguments are the core parts of each individual DisplayField that inform the front end on how to render the text label and value of the field.
int rate = 1.125
int amount = 10000 // 10,000
int total = rate * amount;
new DisplayField()
.label("Rate x Amount") (1)
.template("{{rate}} x {{amount}} = {{total}}") (2)
.arguments(Map.ofEntries( (3)
Map.entry("rate", new DisplayFieldArgument(rate, DisplayFieldArgument.TypeEnum.NUMBER))
Map.entry("amount", new DisplayFieldArgument(amount, DisplayFieldArgument.TypeEnum.NUMBER))
Map.entry("total", new DisplayFieldArgument(total, DisplayFieldArgument.TypeEnum.NUMBER))
))
1 | The label to be shown in the front end. |
2 | A template string that becomes the value for the field. The keys surrounded by double curly brackets (e.g. {{rate}} ) are replaced with the values of the corresponding arguments (C). |
3 | A map of arguments to be interpolated into the template (B). |
When template interpolation and formatting has been applied in the front end, the example above would look something like this:
- Rate x Amount
-
1.125 x 10,000 = 11,250
Translation tokens
To ensure that DisplayFields are translatable along with other content in the front end, DisplayField labels and arguments support the use of i18n tokens.
The front end automatically recognises i18n tokens in the label of a DisplayField, but arguments with i18n tokens must use the argument type TOKEN
for an i18n token to be recognised.
For a full list of argument types, see Argument types.
new DisplayField()
.label("display-field-label.trade-status") (1)
.template("{{status}}") (2)
.arguments(Map.ofEntries( (3)
Map.entry("status", new DisplayFieldArgument("display-field-value.trade-status-pending", DisplayFieldArgument.TypeEnum.TOKEN)) (3)
))
1 | A DisplayField label using the i18n token display-field-label.trade-status . |
2 | The template uses the value of the status argument below (3). |
3 | A single argument status is used. The value of the argument is display-field-value.trade-status-pending , and it uses the argument type TOKEN . |
If the application had i18n translations for both English and Spanish, the example above might display as:
- Trade Status
-
Pending
Or alternatively:
- Estado Comercial
-
Pendiente
Note that you must provide i18n translations for each of the languages and translation tokens you wish to support.
Argument types
Each DisplayField be of one of several data types. The types inform the front end how it should format and display the fields. The following types are available via DisplayFieldArgument.TypeEnum
.
TEXT
-
A plaintext value that should be displayed unmodified. TOKEN is preferred for text values that need to be translatable.
TOKEN
-
An i18n translation token for text translations.
NUMBER
-
A numerical value, such as 1.0, 100, or 1,000. The front end should apply formatting appropriate to the user’s locale.
DATE
-
An ISO date (
YYYY-MM-DD
). The front end should apply formatting appropriate to the user’s locale. TIME
-
An ISO date-time (
YYYY-MM-DD[T]HH:mm:ss.SSS
) or time (HH:mm:ss.SSS
) with or without milliseconds. Input must be in UTC. Milliseconds will be displayed only if the input format contained milliseconds. The front end will convert the value to local time and display the time part only, formatted according to the user’s locale settings. DATETIME
-
An ISO date-time (
YYYY-MM-DD[T]HH:mm:ss.SSS
) with or without milliseconds. Input must be in UTC. Milliseconds will be displayed only if the input format contained milliseconds. Frontend will convert to local time and display the date and time formatted according to the user’s locale settings. RATE
-
A rate value that may be formatted with adapter-provided formatting attributes.
POINTS
-
An amount value that may be formatted with attributes provided by an adapter.
AMOUNT
-
An amount value that may be formatted with attributes provided by an adapter.
Grouping DisplayFields into sections
DisplayFields can be grouped into sections by nesting them under other DisplayFields that act as wrappers. Headers can be added to these sections using the template
property to interpolate a header label.
new DisplayFields()
.displayFields(List.of(
new DisplayFields() (1)
.template("{{header}}") (2)
.arguments(Map.ofEntries( (3)
Map.entry("header", new DisplayFieldArgument("User Info", DisplayFieldArgument.TypeEnum.TEXT))
))
.displayFields(List.of( (4)
new DisplayField()
.label("Account")
.template("{{account}}")
.arguments(Map.ofEntries(
Map.entry("account", new DisplayFieldArgument("User 1", DisplayFieldArgument.TypeEnum.TEXT))
))
)),
new DisplayFields() (5)
.template("{{header}}") (6)
.arguments(Map.ofEntries( (7)
Map.entry("header", new DisplayFieldArgument("Trade Details", DisplayFieldArgument.TypeEnum.TEXT))
))
.displayFields(List.of( (8)
new DisplayField()
.label("Settlement Date")
.template("{{settlement}}")
.arguments(Map.ofEntries(
Map.entry("settlement", new DisplayFieldArgument("2024-04-01", DisplayFieldArgument.TypeEnum.DATE))
))
))
));
1 | The beginning of the first section. |
2 | The template uses the header argument. This will act as the header for the first section. |
3 | The header argument is defined, to be used in the template (2). |
4 | The fields for this section are added. |
5 | The beginning of the second section. |
6 | The template uses the header argument. This will act as the header for the second section. |
7 | The header argument is defined, to be used in the template (6). |
8 | The fields for this section are added. |
When template interpolation and formatting has been applied in the front end, the example above would look something like this:
- User Info
-
- Account
-
User 1
- Trade Details
-
- Settlement Date
-
01/04/2024
DisplayFields in table format
Some messages support DisplayFields in table format.
You can use the DefaultTableFields
helpers to help you create tables for these messages.
See below a simplified version of a DisplayFields table being added to a PostTradeHistory
message. For a full example, see ExamplePostTradeHistoryProvider
in the FX Example Adapter.
public class ExamplePostTradeHistoryProvider implements CachedObjectProvider<PostTradeHistory, TradeSubjectInfo> {
@Override
public void onRequest(final TradeSubjectInfo subjectInfo, final SubjectConsumer<PostTradeHistory> publishObject) {
final Table postTradeHistoryTable = DefaultTableFields.createPostTradeHistoryTable();
DefaultTableFields.addPostTradeHistoryRow(
postTradeHistoryTable,
LocalDate.now(),
new FormattedAmount(new BigDecimal("1000"), "GBP", 2),
LocalDate.now().plusDays(7),
Tenor._1W.toString(),
new FormattedAmount(new BigDecimal("0"), "GBP", 2),
new PostTradeHistoryAction(TypeEnum.ROLL_FORWARD, "0987654321")
);
final PostTradeHistory postTradeHistory = new PostTradeHistory();
postTradeHistory.setTable(postTradeHistoryTable);
publishObject.onNext(postTradeHistory);
}
}
Anatomy of a DisplayFields table
TableCell and TableRow objects use templates and arguments in the same way as regular DisplayFields. See Labels, templates, and arguments for details.
new Table() (1)
.headers(List.of( (2)
new TableCell()
.template("{{header}}")
.arguments(
Map.ofEntries(
Map.entry("header", new DisplayFieldArgument("Header 1", DisplayFieldArgument.TypeEnum.TEXT))
)
),
new TableCell()
.template("{{header}}")
.arguments(
Map.ofEntries(
Map.entry("header", new DisplayFieldArgument("Header 2", DisplayFieldArgument.TypeEnum.TEXT))
)
)
))
.rows(List.of( (3)
new TableRow()
.cells(List.of( (4)
new TableCell()
.template("{data}}")
.arguments(Map.ofEntries(
Map.entry("data", new DisplayFieldArgument("Value 1", DisplayFieldArgument.TypeEnum.TEXT))
)),
new TableCell()
.template("{data}}")
.arguments(Map.ofEntries(
Map.entry("data", new DisplayFieldArgument("Value 2", DisplayFieldArgument.TypeEnum.TEXT))
))
))
));
1 | Create a Table object. |
2 | Using Table.headers , provide a list of TableCell objects. These become headers in the table. |
3 | Using Table.rows , provide a list of TableRow objects. These become rows in the table. |
4 | Each row has a list of TableCell objects, populating each of the columns in that row. |
The example above creates a table with the following structure:
Header 1 | Header 2 |
---|---|
Value 1 |
Value 2 |