MDA portlets
Model-driven architecture (MDA) portlets are useful for implementing commonly repeated UI patterns such as lists, details, filtering, navigation, pickers, cards, charts, and so on. They are designed using Model Definition Language elements, such as portlet, dialog, layout, table, button, and so on, and let you declare what you want your UI to do, without having to worry about the responsiveness, theming, testability, localization, performance instrumentation, and so on.
The inner workings of portlets depend on three major concepts:
- View - the visual layout of the portlet (labels, tables, buttons, and so on) whose underlying model is bound to a class in the business model. It can raise events to the controller when the user interacts with it, for example, presses a button or selects a menu item. It is available in script as (@ view).
- Model - the information from the business model that the portlet (view) displays. It can raise events to the controller when its data or current selection changes. It is available in script as (@ model).
- Controller - the portlet's logic. It handles initialization and events coming from the View (UIEvents > UIActions) and the Model (DataActions). It is available in script as (@ <name> : <arg1> <arg2> ... <argN>) where <name> is a method on the controller API. For more information, see UX Scripting.
View
View is what we see when we declare some XML and it gets rendered into HTML, JavaScript, and CSS in your browser. The following XML renders a List control with the menu, sections, avatars, captions descriptions, info, comments, and field elements.
<Layout class="TaskEntity">
...
...
<Composite name="tabTasks">
<List head="true" items="(@)" name="gridActs" order="(endTime . #t)" viewAll="navigateToWorkspace">
<Menu name="rightClickMenu">
<Item caption="idsa.UIEvents.markCompleted" event="markCompletedTaskEntity" name="markCompletedItem"/>
<Item caption="ids.record.remove" event="sysRemove" name="deleteItem"/>
<Item caption="ids.edit" event="sysProperties" icon="edit" name="propertiesItem"/>
<Item caption="idsf.EntityActList.addFollowUp" event="addRelatedFollowUp" icon="followUp" name="addFollowUp"/>
<Item caption="idsf.EntityActList.addCallRecord" event="addRelatedCallRecord" icon="history" name="addCallRecordItem"/>
</Menu>
<Sections values="act endTimeStatusStr"/>
<Avatars codes="overdueStyle" icons="template icon"/>
<Captions values="title"/>
<Descriptions values="entityParticipantsStr"/>
<Info values="tagSummary"/>
<Comments values="act startDueDateStr"/>
<Field caption="IDS_NOTES" name="gridColumn" values="act text"/>
</List>
</Composite>
</Layout>
This code renders as:
As mentioned, the view is HTML code that gets rendered. If we were to look at the generated HTML in the browser (usually F12), we would see that the List and its properties have been converted into all kinds of DIV, SPAN, and similar elements with appropriate classes and CSS for the target device. Each component in the UI is a view. Each control, container, the portlet itself, and so on, all have their own model, view, and controller.
You may also notice that we declared some menu items for Mark as Complete, Delete, Edit, Add Follow-up, and Add Call Record. The attributes on the menu item elements include a caption, icon, and the event to raise when the menu is selected by the end user. This event (a UIEvent, for example, addRelatedFollowUp
) is raised and then handled by a UIAction in the Controller.
Model
The model can be easily seen in the example above. The layout binds to the TaskEntity
class.
<Layout class="TaskEntity">
Which gives the controls in the layout access to the attributes and associated classes of TaskEntity
.
When we specify the captions of the list as <Captions values="title"/>
, we declare that we want to show the title attribute of each task in the bold caption of the list control.
The order="(endTime . #t)"
attribute on the list control says to sort all of the items in the list by the task's endTime
property.
We are not limited to binding to attributes on the TaskEntity
class. We can traverse through the model to associated classes. By specifying <Sections values="act endTimeStatusStr"/>,
we set the grouping of items by the associated Act's endTimeStatusStr
attribute which returns values like "Overdue", "One week ago", "Today", or "Future".
The model, view, and controller also take care of refreshes, updates, deletes, visibility, and business logic encoded in the model.
Changes in the model are available to the controller. When a user changes the current selection, or changes an attribute on the class, the model can notify the Controller.
Controller
The controller is the portlet's logic. It takes care of:
- Initialization - a script that is run on the initial load.
- Handles any UIEvents raised by the View with UIActions.
- Registers interest in, and handles changes that occur to the Model with DataActions.
It also provides a rich API for performing any client-side scripting required in the three cases above, with methods for interacting with the model, the view, and the context bus. The most common API methods are discussed in UX Scripting.
Next steps
Moving forward, we will look at portlets and dialogs in detail. Specifically, we will learn about:
- Portlet and dialog structure
- UIEvents, UIActions, and data actions
- Menus and toolbars
- Context bus
- Scripting
We will try some examples of common patterns:
- Navigator portlets with filtering
- Detail portlets, including summary cards, charts, and details.
- Banner portlets
- Dialogs
- Pickers
- Creating a new instance
- Editing
- Class-less portlets
- and more
We will also learn about each of the UI controls.