blob: b6295de792f5d0cb6cd563d6f0c8759eaf62b02b [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>UI Layout &amp; Hints :: Apache Isis</title>
<link rel="canonical" href="https://isis.apache.org/userguide/2.0.0-M5/fun/ui.html">
<meta name="generator" content="Antora 2.3.4">
<link rel="stylesheet" href="../../../_/css/site.css">
<link rel="stylesheet" href="../../../_/css/site-custom.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,700,700i|Raleway:300,400,500,700,800|Montserrat:300,400,700" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css"/>
<link rel="home" href="https://isis.apache.org" title="Apache Isis">
<link rel="next" href="business-rules.html" title="Business Rules">
<link rel="prev" href="object-members.html" title="Properties, Collections &amp;amp; Actions">
</head>
<body class="article">
<header class="header">
<nav class="navbar">
<div class="navbar-brand">
<a class="navbar-item" href="https://isis.apache.org">
<span class="icon">
<img src="../../../_/img/isis-logo-48x48.png"></img>
</span>
<span>Apache Isis</span>
</a>
<button class="navbar-burger" data-target="topbar-nav">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div id="topbar-nav" class="navbar-menu">
<a class="navbar-end">
<div class="navbar-item hide-for-print">
<span>
<input id="algolia-search-input" placeholder="Search"></span>
</span>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Quick Start</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Starter Apps</span>
<a class="navbar-item" href="../../../docs/2.0.0-M5/starters/helloworld.html">Hello World</a>
<a class="navbar-item" href="../../../docs/2.0.0-M5/starters/simpleapp.html">Simple App</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Demos &amp; Tutorials</span>
<a class="navbar-item" href="../../../docs/2.0.0-M5/demo/about.html">Demo App</a>
<a class="navbar-item" href="https://danhaywood.gitlab.io/isis-petclinic-tutorial-docs/petclinic/1.16.2/intro.html">Petclinic (tutorial)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Resources</span>
<a class="navbar-item" href="../../../docs/2.0.0-M5/resources/cheatsheet.html">Cheatsheet</a>
<a class="navbar-item" href="../../../docs/2.0.0-M5/resources/icons.html">Icons</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Guides</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Development</span>
<a class="navbar-item" href="../../../setupguide/2.0.0-M5/about.html">Setup Guide</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Core</span>
<a class="navbar-item" href="../../../userguide/2.0.0-M5/about.html">User Guide</a>
<a class="navbar-item" href="../../../refguide/2.0.0-M5/about.html">Reference Guide</a>
<a class="navbar-item" href="../../../testing/2.0.0-M5/about.html">Testing Guide</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Libraries</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">For Use in Apps</span>
<a class="navbar-item" href="../../../subdomains/2.0.0-M5/about.html">Subdomain Libraries</a>
<a class="navbar-item" href="../../../valuetypes/2.0.0-M5/about.html">Value Types</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Integrate between Apps</span>
<a class="navbar-item" href="../../../mappings/2.0.0-M5/about.html">Bounded Context Mapping Libraries</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Other</span>
<a class="navbar-item" href="../../../incubator/2.0.0-M5/about.html">Incubator</a>
<a class="navbar-item" href="../../../legacy/2.0.0-M5/about.html">Legacy</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Components</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Viewers</span>
<a class="navbar-item" href="../../../vw/2.0.0-M5/about.html">Web UI (Wicket)</a>
<a class="navbar-item" href="../../../vro/2.0.0-M5/about.html">REST API (Restful Objects)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Security</span>
<a class="navbar-item" href="../../../security/2.0.0-M5/about.html">Security Guide</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Persistence</span>
<a class="navbar-item" href="../../../pjpa/2.0.0-M5/about.html">JPA (EclipseLink)</a>
<a class="navbar-item" href="../../../pjdo/2.0.0-M5/about.html">JDO (DataNucleus)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Extensions</span>
<a class="navbar-item" href="../../../extensions/2.0.0-M5/about.html">Extensions Catalog</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Support</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Contact</span>
<a class="navbar-item" href="../../../docs/2.0.0-M5/support/slack-channel.html">Slack</a>
<a class="navbar-item" href="../../../docs/2.0.0-M5/support/mailing-list.html">Mailing Lists</a>
<a class="navbar-item" href="https://issues.apache.org/jira/browse/ISIS">JIRA</a>
<a class="navbar-item" href="https://stackoverflow.com/questions/tagged/isis">Stack Overflow</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Releases</span>
<a class="navbar-item" href="../../../docs/2.0.0-M5/downloads/how-to.html">Downloads</a>
<a class="navbar-item" href="../../../relnotes/2.0.0-M5/about.html">Release Notes</a>
<a class="navbar-item" href="../../../docs/2.0.0-M5/archive/1-x.html">Archive (1.x)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Framework</span>
<a class="navbar-item" href="../../../conguide/2.0.0-M5/about.html">Contributors' Guide</a>
<a class="navbar-item" href="../../../comguide/2.0.0-M5/about.html">Committers' Guide</a>
<a class="navbar-item" href="../../../core/2.0.0-M5/about.html">Core Design</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">ASF</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="http://www.apache.org/">Apache Homepage</a>
<a class="navbar-item" href="https://www.apache.org/events/current-event">Events</a>
<a class="navbar-item" href="https://www.apache.org/licenses/">Licenses</a>
<a class="navbar-item" href="https://www.apache.org/security/">Security</a>
<a class="navbar-item" href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a>
<a class="navbar-item" href="https://www.apache.org/foundation/thanks.html">Thanks</a>
<hr class="navbar-divider"/>
<a class="navbar-item" href="https://whimsy.apache.org/board/minutes/Isis.html">PMC board minutes</a>
</div>
</div>
<a class="navbar-item" href="../../../docs/2.0.0-M5/about.html">
<span class="icon">
<img src="../../../_/img/home.png"></img>
</span>
</a>
</div>
</div>
</nav>
</header>
<div class="body ">
<div class="nav-container" data-component="userguide" data-version="2.0.0-M5">
<aside class="nav">
<div class="panels">
<div class="nav-panel-pagination">
<a class="page-previous" rel="prev" href="object-members.html" title="Properties, Collections &amp;amp; Actions"><span></span></a>
<a class="page-next" rel="next"
href="business-rules.html" title="Business Rules"><span></span></a>
<!--
page.parent doesn't seem to be set...
<a class="page-parent disabled" rel="prev" href="" title="Properties, Collections &amp;amp; Actions"><span></span></a>
-->
</div>
<div class="nav-panel-menu is-active" data-panel="menu">
<nav class="nav-menu">
<h3 class="title"><a href="../about.html">User Guide</a></h3>
<ul class="nav-list">
<li class="nav-item" data-depth="0">
<ul class="nav-list">
<li class="nav-item" data-depth="1">
<a class="nav-link" href="concepts-patterns.html">Concepts &amp; Patterns</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="overview.html">Overview</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="domain-entities-and-services.html">Domain Entities &amp; Services</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="object-members.html">Properties, Collections &amp; Actions</a>
</li>
<li class="nav-item is-current-page" data-depth="1">
<a class="nav-link" href="ui.html">UI Layout &amp; Hints</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="business-rules.html">Business Rules</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="drop-downs-and-defaults.html">Drop downs and Defaults</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="meta-annotations.html">Meta-annotations</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="view-models.html">View Models</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="mixins.html">Mixins</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="modules.html">Modules</a>
</li>
<li class="nav-item" data-depth="1">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../btb/about.html">Beyond the Basics</a>
<ul class="nav-list">
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../btb/i18n.html">i18n</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../btb/headless-access.html">Headless Access</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../btb/hints-and-tips.html">Hints-n-Tips</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../btb/programming-model.html">Programming Model</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="1">
<button class="nav-item-toggle"></button>
<span class="nav-text">Extensions</span>
<ul class="nav-list">
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../command-log/about.html">Command Log</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../command-replay/about.html">Command Replay</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../flyway/about.html">Flyway</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../model-annotation/about.html">Model Annotation</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../quartz/about.html">Quartz</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</div>
<div class="nav-panel-explore" data-panel="explore">
<div class="context">
<span class="title">User Guide</span>
<span class="version">2.0.0-M5</span>
</div>
<ul class="components">
<li class="component">
<span class="title"> </span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../docs/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">BC Mapping Libraries</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../mappings/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Committers' Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../comguide/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Contributors' Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../conguide/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Design Docs</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../core/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Extensions Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../extensions/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Incubator Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../incubator/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">JDO/DataNucleus</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../pjdo/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">JPA</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../pjpa/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Legacy Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../legacy/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Reference Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../refguide/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Release Notes</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../relnotes/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">REST API (Restful Objects Viewer)</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../vro/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Security Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../security/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Setup Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../setupguide/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Subdomains Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../subdomains/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Testing Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../testing/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Tooling</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../tooling/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component is-current">
<span class="title">User Guide</span>
<ul class="versions">
<li class="version is-current is-latest">
<a href="../about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Value Types Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../valuetypes/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Web UI (Wicket Viewer)</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../vw/2.0.0-M5/about.html">2.0.0-M5</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</aside>
</div>
<main role="main">
<div class="toolbar" role="navigation">
<button class="nav-toggle"></button>
<a href="../../../docs/2.0.0-M5/about.html" class="home-link"></a>
<nav class="breadcrumbs" aria-label="breadcrumbs">
<ul>
<li><a href="../about.html">User Guide</a></li>
<li><a href="ui.html">UI Layout &amp; Hints</a></li>
</ul>
</nav>
<div class="edit-this-page"><a href="https://github.com/apache/isis/edit/2.0.0-M5/antora/components/userguide/modules/fun/pages/ui.adoc">Edit</a></div>
</div>
<article class="doc">
<a name="section-top"></a>
<h1 class="page">UI Layout &amp; Hints</h1>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>In implementing the <a href="http://en.wikipedia.org/wiki/Naked_objects">naked objects pattern</a>, Apache Isis infers as much information from the domain classes as possible.
Nevertheless, some metadata relating solely to the UI is inevitably required.</p>
</div>
<div class="paragraph">
<p>The Apache Isis programming model includes several mechanisms to influence the way in which the domain objects are rendered in the UI.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the layout of application menu items (inferred from <a href="domain-entities-and-services.html#domain-services" class="page">domain services</a>)</p>
</li>
<li>
<p>the layout of domain objects (<a href="domain-entities-and-services.html#domain-entities" class="page">domain entities</a> and<a href="#view-modules.adoc" class="page unresolved">view models</a>
) allow the positioning of the <a href="object-members.html" class="page">object members</a> into columns and tabs</p>
</li>
<li>
<p>UI hints provided by the domain object itself returning:</p>
<div class="ulist">
<ul>
<li>
<p>the title so the end-user can distinguish one object from another</p>
</li>
<li>
<p>an icon to indicate the type of object (and perhaps its state)</p>
</li>
<li>
<p>CSS styles for additional adhoc styling</p>
</li>
<li>
<p>an alternate layout, for example changed according to the roles of the end-user that is viewing it.</p>
</li>
</ul>
</div>
</li>
<li>
<p>in collections, how to customise which properties of the associated object appear as columns.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This chapter discusses these topics.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="names-and-descriptions"><a class="anchor" href="#names-and-descriptions"></a>Names and Descriptions</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The name of classes and class members are usually inferred from the Java source code directly.
For example, an action method called <code>placeOrder</code> will be rendered as "Place Order", and a collection called <code>orderItems</code> is rendered as "Order Items".
The same is true for action parameter names also, though note that the code must be compiled with the <code>--parameters</code> flag (to javac).</p>
</div>
<div class="paragraph">
<p>Occasionally though the desired name is not possible; either the name is a Java reserved word (eg "class"), or might require characters that are not valid, for example abbreviations.</p>
</div>
<div class="paragraph">
<p>In such cases the name can be specified declaratively.
It is also possible to specify a description declaratively; this is used as a tooltip in the UI.</p>
</div>
<div class="paragraph">
<p>The table below summarizes the annotations available:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 1. Names and descriptions</caption>
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Feature</th>
<th class="tableblock halign-left valign-top">Named</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p>Class</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainObjectLayout.html#named" class="page">@DomainObjectLayout#named</a></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainObjectLayout.html#describedAs" class="page">@DomainObjectLayout#describedAs</a></p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p>Property</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/PropertyLayout.html#named" class="page">@PropertyLayout#named</a></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/PropertyLayout.html#describedAs" class="page">@PropertyLayout#describedAs</a></p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p>Collection</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/CollectionLayout.html#named" class="page">@CollectionLayout#named</a></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/CollectionLayout.html#describedAs" class="page">@CollectionLayout#describedAs</a></p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p>Action</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/ActionLayout.html#named" class="page">@ActionLayout#named</a></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/ActionLayout.html#describedAs" class="page">@ActionLayout#describedAs</a></p>
</div></div></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p>Action Parameters</p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/ParameterLayout.html#named" class="page">@ParameterLayout#named</a></p>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="paragraph">
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/ParameterLayout.html#describedAs" class="page">@ParameterLayout#describedAs</a></p>
</div></div></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>The framework also supports i18n: locale-specific names and descriptions.
For more information, see the <a href="../btb/i18n.html" class="page">beyond-the-basics</a> guide.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="object-titles-and-icons"><a class="anchor" href="#object-titles-and-icons"></a>Titles, Icons etc.</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In Apache Isis every object is identified to the user by a title (label) and an icon.
This is shown in several places: as the main heading for an object; as a link text for an object referencing another object, and also in tables representing collections of objects.</p>
</div>
<div class="paragraph">
<p>The icon is often the same for all instances of a particular class, but it&#8217;s also possible for an individual instance to return a custom icon.
This could represent the state of that object (eg a shipped order, say, or overdue library book).</p>
</div>
<div class="paragraph">
<p>It is also possible for an object to provide a CSS class hint.
In conjunction with <a href="../../../vw/2.0.0-M5/customisation.html#custom-css" class="page">customized CSS</a> this can be used to apply arbitrary styling; for example each object could be rendered in a page with a different background colour.</p>
</div>
<div class="paragraph">
<p>Finally, a domain object can even indicate the <a href="#object-layout">layout</a> that should be used to render it.
For example, a domain object may be rendered differently depending upon the role of the user viewing it.</p>
</div>
<div class="sect2">
<h3 id="object-title"><a class="anchor" href="#object-title"></a>Object Title</h3>
<div class="paragraph">
<p>The object title is a label to identify an object to the end-user.
Generally the object title is a label to identify an object to the end-user.
There is no requirement for it to be absolutely unique, but it should be "unique enough" to distinguish the object from other object&#8217;s likely to be rendered on the same page.</p>
</div>
<div class="paragraph">
<p>The title is always shown with an icon, so there is generally no need for the title to include information about the object&#8217;s type.
For example the title of a customer object shouldn&#8217;t include the literal string "Customer"; it can just have the customer&#8217;s name, reference or some other meaningful business identifier.</p>
</div>
<div class="sect3">
<h4 id="declarative-style"><a class="anchor" href="#declarative-style"></a>Declarative style</h4>
<div class="paragraph">
<p>The <a href="../../../refguide/2.0.0-M5/applib/index/annotation/Title.html" class="page">@Title</a> annotation can be used build up the title of an object from its constituent parts.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import lombok.Getter;
public class Customer {
@Title(sequence="1", append=" ") <i class="conum" data-value="1"></i><b>(1)</b>
@Getter
private String firstName;
@Title(sequence="2") <i class="conum" data-value="2"></i><b>(2)</b>
@Getter
private String lastName;
// ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>First component of the title, with a space afterwards</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Second component of the title</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>might return "Arthur Clarke", while:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import lombok.Getter;
public class CustomerAlt {
@Title(sequence="2", prepend=", ")
@Getter
private String firstName;
@Title(sequence="1")
@Getter
private String lastName;
// ...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>could return "Clarke, Arthur".</p>
</div>
<div class="paragraph">
<p>Note that the sequence is in Dewey Decimal Format, which allows a subclass to intersperse information within the title.
For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import lombok.Getter;
public class Author extends Customer {
@Title(sequence="1.5", append=". ") <i class="conum" data-value="1"></i><b>(1)</b>
@Getter
private String middleInitial;
// ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>"Slots" between the components defined by the superclass</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>could return "Arthur C. Clarke".</p>
</div>
</div>
<div class="sect3">
<h4 id="imperative-style"><a class="anchor" href="#imperative-style"></a>Imperative style</h4>
<div class="paragraph">
<p>Alternatively, the title can be provided simply by implementing the <a href="../../../refguide/2.0.0-M5/applib-methods/ui-hints.html#title" class="page">title()</a> reserved method.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class Author extends Customer {
public String title() {
final StringBuilder buf = new StringBuilder();
buf.append(getFirstName());
if(getMiddleInitial() != null) {
buf.append(getMiddleInitial()).append(". ");
}
buf.append(getLastName();
return buf.toString();
}
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>A variation on this approach also supports localized names; see <a href="../btb/i18n.html" class="page">beyond-the-basics</a> guide for further details.</p>
</div>
</div>
<div class="sect3">
<h4 id="using-a-ui-subscriber"><a class="anchor" href="#using-a-ui-subscriber"></a>Using a UI subscriber</h4>
<div class="paragraph">
<p>A third alternative is to move the responsibility for deriving the title into a separate subscriber object.</p>
</div>
<div class="paragraph">
<p>In the target object, we define an appropriate event type and use the <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainObjectLayout.html#titleUiEvent" class="page">@DomainObjectLayout#titleUiEvent()</a> attribute to specify:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@DomainObjectLayout(
titleUiEvent = Author.TitleUiEvent.class
)
public class Author extends Customer {
public static class TitleUiEvent
extends org.apache.isis.applib.events.ui.TitleUiEvent&lt;Author&gt; {}
//...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The subscriber can then populate this event:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.val;
@Service
public class AuthorSubscriptions {
@EventListener(Author.TitleUiEvent.class)
public void on(Author.TitleUiEvent ev) {
val author = ev.getSource();
ev.setTitle(titleOf(author));
}
private String titleOf(Author author) {
val buf = new StringBuilder();
buf.append(author.getFirstName());
if(author.getMiddleInitial() != null) {
buf.append(author.getMiddleInitial()).append(". ");
}
buf.append(author.getLastName());
return buf.toString();
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>UI listeners are useful when using third-party libraries or extensions.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="object-icon"><a class="anchor" href="#object-icon"></a>Object Icon</h3>
<div class="paragraph">
<p>The icon is often the same for all instances of a particular class, and is picked up by convention.</p>
</div>
<div class="paragraph">
<p>It&#8217;s also possible for an individual instance to return a custom icon, typically so that some significant state of that domain object is represented.
For example, a custom icon could be used to represent a shipped order, say, or an overdue library loan.</p>
</div>
<div class="sect3">
<h4 id="declarative-style-2"><a class="anchor" href="#declarative-style-2"></a>Declarative style</h4>
<div class="paragraph">
<p>If there is no requirement to customize the icon (the normal case), then the icon is usually picked up as the <code>.png</code> file in the same package as the class.
For example, the icon for a class <code>org.mydomain.myapp.Customer</code> will be <code>org/mydomain/myapp/Customer.png</code> (if it exists).</p>
</div>
<div class="paragraph">
<p>Alternatively, a font-awesome icon can be used.
This is specified using the <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainObjectLayout.html#cssClassFa" class="page">@DomainObjectLayout#cssClassFa()</a> attribute or in the <a href="#object-layout">layout.xml</a> file.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@DomainObjectLayout( cssClassFa="play" ) <i class="conum" data-value="1"></i><b>(1)</b>
public class InvoiceRun {
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>will use the "fa-play" icon.</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="imperative-style-2"><a class="anchor" href="#imperative-style-2"></a>Imperative style</h4>
<div class="paragraph">
<p>To customise the icon on an instance-by-instance basis, we implement the reserved <a href="../../../refguide/2.0.0-M5/applib-methods/ui-hints.html#iconName" class="page">iconName()</a> method.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class Order {
public String iconName() {
return isShipped() ? "shipped": null;
}
// ..
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In this case, if the <code>Order</code> has shipped then the framework will look for an icon image named "Order-shipped.png" (in the same package as the class).
Otherwise it will just use "Order.png", as normal.</p>
</div>
</div>
<div class="sect3">
<h4 id="using-a-ui-subscriber-2"><a class="anchor" href="#using-a-ui-subscriber-2"></a>Using a UI subscriber</h4>
<div class="paragraph">
<p>As for title, the determination of which image file to use for the icon can be externalized into a UI event subscriber.</p>
</div>
<div class="paragraph">
<p>In the target object, we define an appropriate event type and use the <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainObjectLayout.html#iconUiEvent" class="page">@DomainObjectLayout#iconUiEvent()</a> attribute to specify.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@DomainObjectLayout(
iconUiEvent = Order.IconUiEvent.class
)
public class Order {
public static class IconUiEvent
extends org.apache.isis.applib.events.ui.IconUiEvent&lt;Order&gt; {}
// ..
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The subscriber can then populate this event:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.val;
@Service
public class OrderSubscriptions {
@EventListener(Order.IconUiEvent.class)
public void on(Order.IconUiEvent ev) {
val order = ev.getSource();
ev.setIconName(iconNameOf(order);
}
private String iconNameOf(Order order) {
return order.isShipped() ? "shipped": null;
}
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="object-css-styling"><a class="anchor" href="#object-css-styling"></a>Object CSS Styling</h3>
<div class="paragraph">
<p>It is also possible for an object to return a <a href="../../../refguide/2.0.0-M5/applib-methods/ui-hints.html#cssclass" class="page">CSS class</a>.
In conjunction with a viewer-specific customisation of CSS (eg for the <a href="../../../vw/2.0.0-M5/about.html" class="page">Web UI (Wicket viewer)</a>, see<a href="../../../vw/2.0.0-M5/customisation.html#custom-css" class="page">here</a>) this can be used to apply arbitrary styling; for example each object could be rendered in a page with a different background colour.</p>
</div>
<div class="sect3">
<h4 id="declarative-style-3"><a class="anchor" href="#declarative-style-3"></a>Declarative style</h4>
<div class="paragraph">
<p>To render an object with a particular CSS, use
<a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainObjectLayout.html#cssClass" class="page">@DomainObjectLayout#cssClass()</a> or in the <a href="#object-layout">layout.xml</a> file.</p>
</div>
<div class="paragraph">
<p>The usage of this CSS class is viewer-specific.
In the case of the <a href="../../../vw/2.0.0-M5/about.html" class="page">Web UI (Wicket viewer)</a>, when the domain object is rendered on its own page, this CSS class will appear on a top-level <code>&lt;div&gt;</code>.
Or, when the domain object is rendered as a row in a collection, then the CSS class will appear in a <code>&lt;div&gt;</code> wrapped by the <code>&lt;tr&gt;</code> of the row.</p>
</div>
<div class="paragraph">
<p>One possible use case would be to render the most important object types with a subtle background colour: <code>Customer</code>s shown in light green, or <code>Order</code>s shown in a light pink, for example.</p>
</div>
</div>
<div class="sect3">
<h4 id="imperative-style-3"><a class="anchor" href="#imperative-style-3"></a>Imperative style</h4>
<div class="paragraph">
<p>To specify a CSS class on an instance-by-instance basis, we implement the reserved <a href="../../../refguide/2.0.0-M5/applib-methods/ui-hints.html#cssclass" class="page">cssClass()</a> method.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class Order {
public String cssClass() {
return isShipped() ? "shipped": null; <i class="conum" data-value="1"></i><b>(1)</b>
}
...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>the implementation might well be the same as the <code>iconName()</code>.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If non-null value is returned then the CSS class will be rendered <em>in addition</em> to any declarative CSS class also specified.</p>
</div>
</div>
<div class="sect3">
<h4 id="using-a-ui-subscriber-3"><a class="anchor" href="#using-a-ui-subscriber-3"></a>Using a UI subscriber</h4>
<div class="paragraph">
<p>As for title and icon, the determination of which CSS class to render can be externalized into a UI event subscriber.</p>
</div>
<div class="paragraph">
<p>In the target object, we define an appropriate event type and use the <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainObjectLayout.html#cssClassUiEvent" class="page">@DomainObjectLayout#cssClassUiEvent()</a> attribute to specify.</p>
</div>
<div class="paragraph">
<p>For example</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@DomainObjectLayout( cssClassUiEvent = Order.CssClassUiEvent.class )
public class Order {
public static class CssClassUiEvent
extends org.apache.isis.applib.events.ui.CssClassUiEvent&lt;Order&gt; {}
// ..
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The subscriber can then populate this event:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.val;
@Service
public class OrderSubscriptions {
@EventListener(Order.CssClassUiEvent.class)
public void on(Order.CssClassUiEvent ev) {
val order = ev.getSource();
ev.setCssClass(cssClassOf(order));
}
private static String cssClassOf(Order order) {
return order.isShipped() ? "shipped": null;
}
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="switching-layouts"><a class="anchor" href="#switching-layouts"></a>Switching Layouts</h3>
<div class="paragraph">
<p>A domain object may also have multiple layouts.
One reason might be based on the role of the user viewing the object; the object members most relevant to a data entry clerk could be quite different to an manager that is viewing, eg to approve it.
The layout could be used to hide some object members, show others.</p>
</div>
<div class="paragraph">
<p>If an alternative layout is indicated (we&#8217;ll look at the mechanics of this below), then this is used to locate an alternative layout file.
For example, if the "edit" layout is specified, then the <code>Xxx.edit.layout.xml</code> file is used (if it exists).</p>
</div>
<div class="paragraph">
<p>More generally, for a given domain object <code>Xxx</code>, if it has specified a layout <code>yyy</code>, then the framework will search for a file <code>Xxx.yyy.layout.xml</code> on the classpath.</p>
</div>
<div class="sect3">
<h4 id="imperative-style-4"><a class="anchor" href="#imperative-style-4"></a>Imperative style</h4>
<div class="paragraph">
<p>To specify the layout on an instance-by-instance basis, we implement the reserved <a href="../../../refguide/2.0.0-M5/applib-methods/ui-hints.html#layout" class="page">layout()</a> method.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class IncomingInvoice {
public String layout() {
return isUserInDataEntryRole() ? "edit": null;
}
...
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-a-ui-subscriber-4"><a class="anchor" href="#using-a-ui-subscriber-4"></a>Using a UI subscriber</h4>
<div class="paragraph">
<p>As for title, icon and CSS, the determination of which layout class to render can be externalized into a UI event subscriber.</p>
</div>
<div class="paragraph">
<p>In the target object, we define an appropriate event type and use the <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainObjectLayout.html#layoutUiEvent" class="page">@DomainObjectLayout#layoutUiEvent()</a> attribute to specify.</p>
</div>
<div class="paragraph">
<p>For example</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@DomainObjectLayout( layoutUiEvent = Order.LayoutUiEvent.class )
public class IncomingInvoice {
public static class LayoutUiEvent
extends org.apache.isis.applib.events.ui.LayoutUiEvent&lt;Order&gt; {}
// ..
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The subscriber can then populate this event:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import lombok.val;
@Service
public class IncomingInvoiceSubscriptions {
@EventListener(IncomingInvoice.LayoutUiEvent.class)
public void on(IncomingInvoice.LayoutUiEvent ev) {
val incomingInvoice = ev.getSource();
ev.setLayout(layoutOf(incomingInvoice));
}
private static String layoutOf(IncomingInvoice incomingInvoice) {
return isUserInDataEntryRole() ? "edit": null;
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="fallback-layouts"><a class="anchor" href="#fallback-layouts"></a>Fallback layouts</h4>
<div class="paragraph">
<p>In addition to searching for alternate layouts and then the default layout, in the absence of either the framework will also search for a "fallback" layouts.</p>
</div>
<div class="paragraph">
<p>The use case is to allow libraries that provide domain objects (for example, the <a href="../../../security/2.0.0-M5/secman/about.html" class="page">SecMan</a> extension) to define the UI of these objects using a layout file, while still allowing the consuming application to override that layout if it so requires.</p>
</div>
<div class="paragraph">
<p>Thus, for a domain object "Xxx", the framework searches:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><em>Xxx.yyy.layout.xml</em></p>
<div class="paragraph">
<p>for layout "yyy" (if a non-null layout "yyy" is specified)</p>
</div>
</li>
<li>
<p><em>Xxx.layout.xml</em></p>
<div class="paragraph">
<p>the default layout</p>
</div>
</li>
<li>
<p><em>Xxx.layout.fallback.xml</em></p>
<div class="paragraph">
<p>the fallback layout</p>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>If none of these exist, then the framework will use a layout based on any available annotations.
The page will be split 4:8, with the first column for properties and the second column for collections.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="action-icons-and-css"><a class="anchor" href="#action-icons-and-css"></a>Action Icons and CSS</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Apache Isis allows <a href="http://fortawesome.github.io/Font-Awesome/icons/">font awesome</a> icons to be associated with each action, and for <a href="http://getbootstrap.com/css/#buttons">Bootstrap CSS</a> to be applied to action rendered as buttons.
These UI hints can be applied either to individual actions, or can be applied en-masse using pattern matching.</p>
</div>
<div class="paragraph">
<p>It is also possible to specify additional CSS for an object&#8217;s members (not just actions).</p>
</div>
<div class="sect2">
<h3 id="icons"><a class="anchor" href="#icons"></a>Icons</h3>
<div class="paragraph">
<p>Action icons can be specified in several ways.</p>
</div>
<div class="paragraph">
<p>One option is to use the <a href="../../../refguide/2.0.0-M5/applib/index/annotation/ActionLayout.html#cssClassFa" class="page">@ActionLayout#cssClassFa</a>.
For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@ActionLayout(cssClassFa="refresh")
public void renew() {
...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Alternatively, you can specify these hints dynamically in the <a href="#object-layout">layout file</a> for the entity:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;cpt:action id="renew" cssClassFa="refresh"/&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Rather than annotating every action with <a href="../../../refguide/2.0.0-M5/applib/index/annotation/ActionLayout.html#cssClassFa" class="page">@ActionLayout#cssClassFa</a> and <a href="../../../refguide/2.0.0-M5/applib/index/annotation/ActionLayout.html#cssClass" class="page">@ActionLayout#cssClass</a> you can instead specify the UI hint globally using regular expressions.
Not only does this save a lot of boilerplate/editing, it helps ensure consistency across all actions.</p>
</div>
<div class="paragraph">
<p>To declare fa classes globally, use the <a href="../../../refguide/2.0.0-M5/config/sections/isis.applib.html#isis.applib.annotation.action-layout.css-class-fa.patterns" class="page">isis.applib.annotation.action-layout.css-class-fa.patterns</a> configuration property (a comma separated list of key:value pairs).</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">isis.applib.annotation.action-layout.css-class-fa.patterns=\
new.*:fa-plus,\
add.*:fa-plus-square,\
create.*:fa-plus,\
renew.*:fa-refresh,\
list.*:fa-list, \
all.*:fa-list, \
download.*:fa-download, \
upload.*:fa-upload, \
execute.*:fa-bolt, \
run.*:fa-bolt</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the key is a regex matching action names (eg <code>create.*</code>), and</p>
</li>
<li>
<p>the value is a <a href="http://fortawesome.github.io/Font-Awesome/icons/">font-awesome</a> icon name</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>For example, "fa-plus" is applied to all action members called "newXxx"</p>
</div>
</div>
<div class="sect2">
<h3 id="css"><a class="anchor" href="#css"></a>CSS</h3>
<div class="paragraph">
<p>Similarly, a CSS class can be specified for object members:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/ActionLayout.html#cssClass" class="page">@ActionLayout#cssClass</a> for actions</p>
</li>
<li>
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/PropertyLayout.html#cssClass" class="page">@PropertyLayout#cssClass</a> for properties, and</p>
</li>
<li>
<p><a href="../../../refguide/2.0.0-M5/applib/index/annotation/CollectionLayout.html#cssClass" class="page">@CollectionLayout#cssClass</a> for collections.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Again, this CSS class will be attached to an appropriate containing <code>&lt;div&gt;</code> or <code>&lt;span&gt;</code> on the rendered page.</p>
</div>
<div class="paragraph">
<p>Possible use cases for this is to highlight the most important properties of a domain object.</p>
</div>
<div class="paragraph">
<p>It is also possible to specify CSS classes globally, using the <a href="../../../refguide/2.0.0-M5/config/sections/isis.applib.html#isis.applib.annotation.action-layout.css-class.patterns" class="page">isis.applib.annotation.action-layout.css-class.patterns</a> configuration property.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">isis.applib.annotation.action-layout.css-class.patterns=\
delete.*:btn-warning</code></pre>
</div>
</div>
<div class="paragraph">
<p>where (again):</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the key is a regex matching action names (eg <code>delete.*</code>), and</p>
</li>
<li>
<p>the value is a <a href="http://getbootstrap.com/css/">Bootstrap</a> CSS button class (eg `btn-warning) to be applied</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="menu-bars-layout"><a class="anchor" href="#menu-bars-layout"></a>Menu Bars Layout</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The actions of <a href="domain-entities-and-services.html#domain-services" class="page">domain services</a> (annotated using <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainService.html" class="page">@DomainService</a> with a <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainService.html#nature" class="page">nature</a> of <code>VIEW</code>) are made available as menu items on menus.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@DomainService(
nature = NatureOfService.VIEW, <i class="conum" data-value="1"></i><b>(1)</b>
objectType = "simple.SimpleObjects"
)
public class SimpleObjects {
// ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Domain service with actions visible in the UI</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>By default each domain service corresponds to a single menu on this menu bar, with its actions as the drop-down menu items.</p>
</div>
<div class="paragraph">
<p>By annotating the domain service class and its actions, it&#8217;s possible to have a little more control over the placement of the menu items; this is discussed <a href="#annotation-based-menus">below</a>.</p>
</div>
<div class="paragraph">
<p>For more fine-grained control, though, the <code>menubars.layout.xml</code> file can be supplied, as discussed <a href="#file-based-menus">after</a>.
This file is also read dynamically at runtime, so layouts can be changed during prototyping and the page reloaded to check.</p>
</div>
<div class="sect2">
<h3 id="annotation-based-menus"><a class="anchor" href="#annotation-based-menus"></a>Annotation-based</h3>
<div class="paragraph">
<p>If using annotations, services can be added to either the primary, secondary or tertiary menu bar, as shown in this screenshot shows:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_images/application-menu/layout-menus.png" alt="layout menus" width="800px">
</div>
</div>
<div class="paragraph">
<p>This is done using the <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainServiceLayout.html#menuBar" class="page">@DomainServiceLayout#menuBar()</a> annotation.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@DomainService(
nature = NatureOfService.VIEW, <i class="conum" data-value="1"></i><b>(1)</b>
objectType = "simple.SimpleObjects"
)
@DomainServiceLayout(menuBar = DomainServiceLayout.MenuBar.PRIMARY) <i class="conum" data-value="2"></i><b>(2)</b>
public class SimpleObjects {
// ...
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Domain service with actions visible in the UI</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Menu for the service added to the primary menu bar.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The tertiary menu bar consists of a single unnamed menu, rendered underneath the user&#8217;s login, top right.
This is intended primarily for actions pertaining to the user themselves, eg their account, profile or settings:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_images/application-menu/tertiary.png" alt="tertiary" width="300px">
</div>
</div>
<div class="paragraph">
<p>In addition, the <a href="../../../refguide/2.0.0-M5/applib/index/annotation/MemberOrder.html" class="page">@MemberOrder</a> annotation can be used to group and order domain service actions:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>@MemberOrder#name()</code> is used to define the name of the menu</p>
<div class="paragraph">
<p>in which the menu items of the domain service action will appear</p>
</div>
</li>
<li>
<p><code>@MemberOrder#sequence()</code> is used to order menu items within that menu.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Thus, using <code>name()</code> it&#8217;s possible to group menu items from actions that are from different domain services.</p>
</div>
<div class="paragraph">
<p>There are some significant limitations, however:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>domain services are added to the menu bar alphabetically.
This cannot be controlled using annotations.</p>
</li>
<li>
<p>there is no way to order or group menu items from multiple domain services that appear on the same menu.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The annotation based approach is therefore useful during very early prototyping, but in real-world applications you should use <a href="#file-based-menus">file based</a> menu layouts.</p>
</div>
</div>
<div class="sect2">
<h3 id="file-based-menus"><a class="anchor" href="#file-based-menus"></a><code>menubars.layout.xml</code></h3>
<div class="paragraph">
<p>Rather than use annotations to specify the location of menu items corresponding to the domain services' actions, the framework instead allow domain service actions to be arranged using the <code>menubars.layout.xml</code> file.</p>
</div>
<div class="paragraph">
<p>This offers a number of benefits:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Probably most significantly, the layout can be updated without requiring a recompile of the code and redeploy of the app; fine-tuning the layout with your end users is easy to do</p>
</li>
<li>
<p>You&#8217;ll probably find it easier to reason about menu bars layout when all the hints are collated together in a single place (rather than scattered across the domain service classes as annotations).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>There are some disadvantages to using file-based layouts:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>file-based layouts are not typesafe: a typo will result in the metadata not being picked up for the element.</p>
</li>
<li>
<p>they also suffer from syntactic fragility: an invalid XML document could result in no metadata for the entire class.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The <code>menubars.layout.xml</code> file is just the serialized form of a <a href="../../../refguide/2.0.0-M5/applib-classes/layout/menubars.html" class="page">MenuBars</a> layout class defined within Apache Isis' applib.
These are JAXB-annotated classes with corresponding XSD schemas; the upshot of that
is that IDEs such as IntelliJ and Eclipse can provide "intellisense", making it easy to author such layout files.</p>
</div>
<div class="paragraph">
<p>For example, here&#8217;s a fragment of that provided by the <a href="../../../docs/2.0.0-M5/starters/simpleapp.html" class="page">SimpleApp</a> starter app:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
&lt;mb3:menuBars
xsi:schemaLocation="..."
xmlns:cpt="http://isis.apache.org/applib/layout/component"
xmlns:lnk="http://isis.apache.org/applib/layout/links"
xmlns:mb3="http://isis.apache.org/applib/layout/menubars/bootstrap3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
&lt;mb3:primary&gt; <i class="conum" data-value="1"></i><b>(1)</b>
&lt;mb3:menu&gt; <i class="conum" data-value="2"></i><b>(2)</b>
&lt;mb3:named&gt;Simple Objects&lt;/mb3:named&gt;
&lt;mb3:section&gt; <i class="conum" data-value="3"></i><b>(3)</b>
&lt;mb3:serviceAction <i class="conum" data-value="4"></i><b>(4)</b>
objectType="simple.SimpleObjects" <i class="conum" data-value="5"></i><b>(5)</b>
id="create"&gt;
&lt;cpt:named&gt;Create&lt;/cpt:named&gt; <i class="conum" data-value="6"></i><b>(6)</b>
&lt;/mb3:serviceAction&gt;
&lt;mb3:serviceAction
objectType="simple.SimpleObjects"
id="findByName"&gt;
&lt;cpt:named&gt;Find By Name&lt;/cpt:named&gt;
&lt;/mb3:serviceAction&gt;
&lt;mb3:serviceAction
objectType="simple.SimpleObjects"
id="listAll"&gt;
&lt;cpt:named&gt;List All&lt;/cpt:named&gt;
&lt;/mb3:serviceAction&gt;
&lt;/mb3:section&gt;
&lt;/mb3:menu&gt;
&lt;mb3:menu unreferencedActions="true"&gt; <i class="conum" data-value="7"></i><b>(7)</b>
&lt;mb3:named&gt;Other&lt;/mb3:named&gt;
&lt;/mb3:menu&gt;
&lt;/mb3:primary&gt;
&lt;mb3:secondary&gt; <i class="conum" data-value="8"></i><b>(8)</b>
&lt;mb3:menu&gt;
&lt;mb3:named&gt;Prototyping&lt;/mb3:named&gt;
...
&lt;/mb3:secondary&gt;
&lt;mb3:tertiary&gt; <i class="conum" data-value="9"></i><b>(9)</b>
&lt;mb3:menu&gt;
...
&lt;/mb3:menu&gt;
&lt;/mb3:tertiary&gt;
&lt;/mb3:menuBars&gt;</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Primary menu bar.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Menu on the menu bar</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>References an action of a domain service</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Divider is placed between each section</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Identifies the service through its <a href="../../../refguide/2.0.0-M5/applib/index/annotation/DomainService.html#objectType" class="page">object type</a></td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Optionally override the name inferred from the action</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Domain service actions not specified elsewhere are displayed on the "Other" menu (with <code>unreferencedActions</code> attribute set to <code>true</code>).<br>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
For a layout file to be valid there must be exactly one <code>&lt;menu&gt;</code> with the
<code>unreferencedActions</code> attribute set.
</td>
</tr>
</table>
</div></td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>Secondary menu bar.</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>Tertiary menu bar.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Any domain service actions that are not explicitly listed will be placed under this menu.</p>
</div>
<div class="paragraph">
<p>The downloaded <code>menubars.layout.xml</code> file can be adjusted as necessary, creating new menus and menu sections.
Once done, it can be saved and the project rebuilt in the IDE.
If running in prototype mode, the file will be dynamically reloaded from the classpath.</p>
</div>
<div class="paragraph">
<p>Once the application has bootstrapped with a layout file, downloading the "Default" layout (from the prototyping menu) in essence just returns this file.</p>
</div>
<div class="sect3">
<h4 id="downloading-the-layout-file"><a class="anchor" href="#downloading-the-layout-file"></a>Downloading the layout file</h4>
<div class="paragraph">
<p>The current <code>menubars.layout.xml</code> can be downloaded from the <a href="../../../refguide/2.0.0-M5/applib/index/services/menu/MenuBarsService.html" class="page">MenuBarsService</a> (exposed on the prototyping menu):</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_images/menubars/010-download.png" alt="010 download" width="300px">
</div>
</div>
<div class="paragraph">
<p>If there are unknown/unreferenced actions in the "Other" menu (which you would like to place elsewhere), then these will be listed in the downloaded layout, so they can easily be moved elsewhere.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="object-layout"><a class="anchor" href="#object-layout"></a>Object Layout</h2>
<div class="sectionbody">
<div class="paragraph">
<p>As with menubars, although the layout of objects can be specified using just annotations, in real-world applications you will almost certainly use a companion layout file, <code>Xxx.layout.xml</code> (where <code>Xxx</code> is the entity or view model to be rendered).</p>
</div>
<div class="paragraph">
<p>File-based layouts offer a number of benefits:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Probably most significantly, the layout can be updated without requiring a recompile of the code and redeploy of the app; fine-tuning the layout with your end users is easy to do</p>
</li>
<li>
<p>Many developers also find it easier to rationalize about layout when all the hints are collated together in a single place (rather than scattered across the class members as annotations).</p>
</li>
<li>
<p>UI hints can be provided for <a href="mixins.html" class="page">mixin</a> contributions that are synthesised at runtime.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>It is also possible to download an initial <code>.layout.xml</code> - capturing any existing layout metadata - using the <a href="../../../refguide/2.0.0-M5/applib/index/services/layout/LayoutService.html" class="page">LayoutService</a> (exposed on the prototyping menu) or using a <a href="#refguide:applib-classes:mixins.adoc#java-lang-object" class="page unresolved">mixin action</a> contributed to every domain object.</p>
</div>
<div class="paragraph">
<p>There are some downsides, though:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>file-based layouts are not typesafe: a typo will result in the metadata not being picked up for the element.</p>
</li>
<li>
<p>they suffer from syntactic fragility: an invalid XML document could result in no metadata for the entire class.</p>
</li>
<li>
<p>there is no notion of inheritance, so a <code>.layout.xml</code> is required for all concrete classes and also for any abstract classes (if used as a collection type).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The <code>Xxx.layout.xml</code> file is just the serialized form of a <a href="../../../refguide/2.0.0-M5/applib-classes/layout/object-layout.html" class="page">Grid</a> layout class defined within Apache Isis' applib.
These are JAXB-annotated classes with corresponding XSD schemas; the upshot of that is that IDEs such as IntelliJ and Eclipse can provide "intellisense", making iteasy to author such layout files.</p>
</div>
<div class="sect2">
<h3 id="grids-vs-components"><a class="anchor" href="#grids-vs-components"></a>Grids vs Components</h3>
<div class="paragraph">
<p>The layout file distinguishes between two types of element:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>those that define a grid structure, of: rows, columns, tab groups and tabs.</p>
<div class="paragraph">
<p>The rows and columns are closely modelled on <a href="http://getbootstrap.com">Bootstrap 3</a> (used in the implementation of the <a href="../../../vw/2.0.0-M5/about.html" class="page">Web UI (Wicket viewer)</a>).</p>
</div>
</li>
<li>
<p>those that define common components, of: fieldsets (previously called member groups or property groups), properties, collections, actions and also the title/icon of the domain object itself.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>More information about these classes can be found in <a href="../../../refguide/2.0.0-M5/applib-classes/layout/object-layout.html" class="page">the reference guide</a>.
More information on Bootstrap 3&#8217;s grid system can be found <a href="http://getbootstrap.com/css/#grid">here</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="by-example"><a class="anchor" href="#by-example"></a>By Example</h3>
<div class="paragraph">
<p>Probably the easiest way to understand dynamic XML layouts is by example, in this case of a "todo item":</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_images/layout-dynamic-xml/ToDoItem.png" alt="ToDoItem" width="940px">
</div>
</div>
<div class="sect3">
<h4 id="namespaces"><a class="anchor" href="#namespaces"></a>Namespaces</h4>
<div class="paragraph">
<p>Every <code>.layout.xml</code> file must properly declare the XSD namespaces and schemas.
There are two: one for the grid classes, and one for the common component classes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
&lt;!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for additional
information regarding copyright ownership. The ASF licenses this file to
you under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License. --&gt;
&lt;bs3:grid
xsi:schemaLocation="http://isis.apache.org/applib/layout/component
http://isis.apache.org/applib/layout/component/component.xsd
http://isis.apache.org/applib/layout/grid/bootstrap3
http://isis.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd"
xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3"
xmlns:c="http://isis.apache.org/applib/layout/component"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
...
&lt;/bs3:grid&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Most IDEs will automatically download the XSD schemas from the specified schema locations, thereby providing "intellisense" help as you edit the file.</p>
</div>
</div>
<div class="sect3">
<h4 id="rows-full-width-cols-and-tabs"><a class="anchor" href="#rows-full-width-cols-and-tabs"></a>Rows, full-width cols, and tabs</h4>
<div class="paragraph">
<p>The example layout consists of three rows: a row for the object/icon, a row containing a properties, and a row containing collections.
In all three cases the row contains a single column spanning the full width of the page.
For the property and collection rows, the column contains a tab group.</p>
</div>
<div class="paragraph">
<p>This corresponds to the following XML:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;bs3:row&gt;
&lt;bs3:col span="12" unreferencedActions="true"&gt;
&lt;c:domainObject bookmarking="AS_ROOT"/&gt;
&lt;/bs3:col&gt;
&lt;/bs3:row&gt;
&lt;bs3:row&gt;
&lt;bs3:col span="12"&gt;
&lt;bs3:tabGroup&gt;
&lt;bs3:tab name="Properties"&gt;...&lt;/bs3:tab&gt;
&lt;bs3:tab name="Other"&gt;...&lt;/bs3:tab&gt;
&lt;bs3:tab name="Metadata"&gt;...&lt;/bs3:tab&gt;
&lt;/bs3:tabGroup&gt;
&lt;/bs3:col&gt;
&lt;/bs3:row&gt;
&lt;bs3:row&gt;
&lt;bs3:col span="12"&gt;
&lt;bs3:tabGroup unreferencedCollections="true"&gt;
&lt;bs3:tab name="Similar to"&gt;...&lt;/bs3:tab&gt;
&lt;bs3:tab name="Dependencies"&gt;...&lt;/bs3:tab&gt;
&lt;/bs3:tabGroup&gt;
&lt;/bs3:col&gt;
&lt;/bs3:row&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>You will notice that one of the <code>col</code>umns has an <code>unreferencedActions</code> attribute, while one of the <code>tabGroup</code>s has a similar <code>unreferencedCollections</code> attribute.
This topic is discussed in more detail <a href="#unreferenced-members">below</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="fieldsets"><a class="anchor" href="#fieldsets"></a>Fieldsets</h4>
<div class="paragraph">
<p>The first tab containing properties is divided into two columns, each of which holds a single fieldset of multiple properties.
Those properties in turn can have associated actions.</p>
</div>
<div class="paragraph">
<p>This corresponds to the following XML:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;bs3:tab name="Properties"&gt;
&lt;bs3:row&gt;
&lt;bs3:col span="6"&gt;
&lt;c:fieldSet name="General" id="general" unreferencedProperties="true"&gt;
&lt;c:action id="duplicate" position="PANEL_DROPDOWN"/&gt;
&lt;c:action id="delete"/&gt;
&lt;c:property id="description"/&gt;
&lt;c:property id="category"/&gt;
&lt;c:property id="subcategory"&gt;
&lt;c:action id="updateCategory"/&gt;
&lt;c:action id="analyseCategory" position="RIGHT"/&gt;
&lt;/c:property&gt;
&lt;c:property id="complete"&gt;
&lt;c:action id="completed" cssClassFa="fa-thumbs-up"/&gt;
&lt;c:action id="notYetCompleted" cssClassFa="fa-thumbs-down"/&gt;
&lt;/c:property&gt;
&lt;/c:fieldSet&gt;
&lt;/bs3:col&gt;
&lt;bs3:col span="6"&gt;
...
&lt;/bs3:col&gt;
&lt;/bs3:row&gt;
&lt;/bs3:tab&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The tab defines two columns, each span of 6 (meaning half the width of the page).</p>
</div>
<div class="paragraph">
<p>In the first column there is a single fieldset.
Notice how actions - such as <code>duplicate</code> and <code>delete</code> - can be associated with this fieldset directly, meaning that they should be rendered on the fieldset&#8217;s top panel.</p>
</div>
<div class="paragraph">
<p>Thereafter the fieldset lists the properties in order.
Actions can be associated with properties too; here they are rendered underneath or to the right of the field.</p>
</div>
<div class="paragraph">
<p>Note also the <code>unreferencedProperties</code> attribute for the fieldset; this topic is discussed in more detail <a href="#unreferenced-members">below</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The <code>&lt;fieldset&gt;</code>'s "name" attribute is optional.
If omitted, then the title panel is suppressed, freeing more real estate.</p>
</div>
<div class="paragraph">
<p>Do be aware though that if there are any actions that have been placed on the fieldset&#8217;s panel, then these <em>will <strong>not</strong> be displayed</em>.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="collections"><a class="anchor" href="#collections"></a>Collections</h4>
<div class="paragraph">
<p>In the final row the collections are placed in tabs, simply one collection per tab.
This corresponds to the following XML:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;bs3:tab name="Similar to"&gt;
&lt;bs3:row&gt;
&lt;bs3:col span="12"&gt;
&lt;c:collection defaultView="table" id="similarTo"/&gt;
&lt;/bs3:col&gt;
&lt;/bs3:row&gt;
&lt;/bs3:tab&gt;
&lt;bs3:tab name="Dependencies"&gt;
&lt;bs3:row&gt;
&lt;bs3:col span="12"&gt;
&lt;c:collection defaultView="table" id="dependencies"&gt;
&lt;c:action id="add"/&gt;
&lt;c:action id="remove"/&gt;
&lt;/c:collection&gt;
&lt;/bs3:col&gt;
&lt;/bs3:row&gt;
&lt;/bs3:tab&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>As with properties, actions can be associated with collections; this indicates that they should be rendered in the collection&#8217;s header.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="unreferenced-members"><a class="anchor" href="#unreferenced-members"></a>Unreferenced Members</h3>
<div class="paragraph">
<p>As noted in the preceding discussion, several of the grid&#8217;s regions have either an <code>unreferencedActions</code>, <code>unreferencedCollections</code> or <code>unreferencedProperties</code> attribute.</p>
</div>
<div class="paragraph">
<p>The rules are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>unreferencedActions</code> attribute can be specified either on a column or on a fieldset.</p>
<div class="paragraph">
<p>It would normally be typical to use the column holding the <code>&lt;domainObject/&gt;</code> icon/title, that is as shown in the example.
The unreferenced actions then appear as top-level actions for the domain object.</p>
</div>
</li>
<li>
<p><code>unreferencedCollections</code> attribute can be specified either on a column or on a tabgroup.</p>
<div class="paragraph">
<p>If specified on a column, then that column will contain each of the unreferenced collections, stacked one on top of the other.
If specified on a tab group, then a separate tab will be created for each collection, with that tab containing only that single collection.</p>
</div>
</li>
<li>
<p><code>unreferencedProperties</code> attribute can be specified only on a fieldset.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The purpose of these attributes is to indicate where in the layout any unreferenced members should be rendered.
Every grid <em>must</em> nominate one region for each of these three member types, the reason being that to ensure that the layout can be used even if it is incomplete with respect to the object members inferred from the Java source code.
This might be because the developer forgot to update the layout, or it might be because of a new mixin (property, collection or action) contributed to many objects.</p>
</div>
<div class="paragraph">
<p>The framework ensures that in any given grid exactly one region is specified for each of the three <code>unreferenced&#8230;&#8203;</code> attributes.
If the grid fails this validation, then a warning message will be displayed, and the invalid XML logged.
The layout XML will then be ignored.</p>
</div>
</div>
<div class="sect2">
<h3 id="combining-with-annotations"><a class="anchor" href="#combining-with-annotations"></a>Combining with Annotations</h3>
<div class="paragraph">
<p>Rather than specify every UI semantic in the layout file, you can optionally combine with a number of annotations.
The idea is that the layout.xml is used primarily for the coarse-grained grid layout, with annotations used for the stuff that changes less often, such as associating actions with properties or collections, or the order of properties or actions within a fieldset.</p>
</div>
<div class="paragraph">
<p>The annotations most relevant here is <a href="../../../refguide/2.0.0-M5/applib/index/annotation/MemberOrder.html" class="page">@MemberOrder</a>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>for properties:</p>
<div class="ulist">
<ul>
<li>
<p><code>@MemberOrder#name()</code> can be used to identify associate a property with a fieldset.</p>
<div class="paragraph">
<p>With this approach all of the fieldsets in the <code>layout.xml</code> file are left empty.
The properties "slot into" the relevant field set to associate = "&#8230;&#8203;", sequence = "&#8230;&#8203;")`.</p>
</div>
</li>
<li>
<p><code>@MemberOrder#sequence()</code> specifies the order of properties within their fieldset</p>
</li>
</ul>
</div>
</li>
<li>
<p>for actions:</p>
<div class="ulist">
<ul>
<li>
<p><code>@MemberOrder#name()</code> is used to associate an action with a property.</p>
<div class="paragraph">
<p>The <code>@Action#associateWith()</code> annotation can be used instead as a direct replacement that "reads" better.</p>
</div>
</li>
<li>
<p><code>@MemberOrder#sequence()</code> specifies the order of actions (if there are multiple actions for a property)</p>
<div class="paragraph">
<p>The <code>@Action#associateWithSequence()</code> annotation can be used as a direct replacement.</p>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="paragraph">
<p>There are a number of other "layout" annotations, specifically <a href="../../../refguide/2.0.0-M5/applib/index/annotation/PropertyLayout.html" class="page">@PropertyLayout</a>, <a href="../../../refguide/2.0.0-M5/applib/index/annotation/CollectionLayout.html" class="page">@CollectionLayout</a> and <a href="../../../refguide/2.0.0-M5/applib/index/annotation/ActionLayout.html" class="page">@ActionLayout</a>.
All of the semantics in these layout annotations can also be specified in the <code>.layout.xml</code> files; which is used is a matter of taste.</p>
</div>
<div class="paragraph">
<p>In addition, <a href="../../../refguide/2.0.0-M5/applib/index/annotation/ParameterLayout.html" class="page">@ParameterLayout</a> provides layout hints for action parameters.
There is no way to specify these semantics in the <code>.layout.xml</code> file (action parameters are not enumerated in the file).</p>
</div>
<div class="sect3">
<h4 id="layout-file-styles"><a class="anchor" href="#layout-file-styles"></a>Layout file styles</h4>
<div class="paragraph">
<p>If you want to make your usage of layout files consistent, then the framework can help because it allows the layout XML files to be downloaded using the <a href="../../../refguide/2.0.0-M5/applib/index/services/layout/LayoutService.html" class="page">LayoutService</a>.
This is exposed on the prototyping menu to allow you to download a ZIP file of layout XML files for all domain entities and view models.</p>
</div>
<div class="paragraph">
<p>When downloading the layout files, there are four "styles" available:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>current</p>
</li>
<li>
<p>complete</p>
</li>
<li>
<p>normalized</p>
</li>
<li>
<p>minimal</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Ignoring the "current" style (which merely downloads the currently cached layout), the other three styles allow the developer to choose how much metadata is to be specified in the XML, and how much (if any) will be obtained from annotations in the metamodel.</p>
</div>
<div class="paragraph">
<p>As a developer, you can therefore select the layout file which maps to your preferred usage:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>if you want all layout metadata to be read from the <code>.layout.xml</code> file, then download the "complete" version, and copy the file alongside the domain class.
You can then remove all <code>@MemberOrder</code>, <code>@ActionLayout</code>, <code>@PropertyLayout</code> and <code>@CollectionLayout</code> annotations from the source code of the domain class.</p>
</li>
<li>
<p>if you want to use layout XML file to describe the grid (columns, tabs etc) and specify which object members are associated with those regions of the grid, then download the "normalized" version.
You can then remove the <code>@MemberOrder</code> and <code>@Action#associateWith</code> annotations from the source code of the domain class, but retain the <code>@ActionLayout</code>, <code>@PropertyLayout</code> and <code>@CollectionLayout</code> annotations.</p>
</li>
<li>
<p>if you want to use layout XML file ONLY to describe the grid, then download the "minimal" version.
The grid regions will be empty in this version, and the framework will use the <code>@MemberOrder</code> and <code>@Action#associateWith</code> annotations to bind object members to those regions.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If you want to adjust the layout XML for a single domain object, then you can use the <a href="#refguide:applib-classes:mixins.adoc" class="page unresolved">mixin action</a> (contributed to every domain object).</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="table-columns"><a class="anchor" href="#table-columns"></a>Table Columns</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The optional <a href="../../../refguide/2.0.0-M5/applib/index/services/tablecol/TableColumnOrderService.html" class="page">TableColumnOrderService</a> SPI service can be used to reorder columns in a table, either for a parented collection (owned by parent domain object) or a standalone collection (returned from an action invocation).</p>
</div>
<div class="paragraph">
<p>For example, suppose there is a <code>Customer</code> and an <code>Order</code>:</p>
</div>
<div class="imageblock kroki">
<div class="content">
<img src="_images/diag-873d2b9971dd50f9575ba49032918f5b93d24513.svg" alt="Diagram">
</div>
</div>
<div class="paragraph">
<p>The order of these properties of <code>Order</code>, when rendered in the context of its owning <code>Customer</code>, can be controlled using this implementation of <code>TableColumnOrderService</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Service
@Order(100) <i class="conum" data-value="1"></i><b>(1)</b>
public class TableColumnOrderServiceForCustomerOrders
implements TableColumnOrderService {
public List&lt;String&gt; orderParented(
final Object parent,
final String collectionId,
final Class&lt;?&gt; collectionType,
final List&lt;String&gt; propertyIds) {
return parent instanceof Customer &amp;&amp; <i class="conum" data-value="2"></i><b>(2)</b>
"orders".equals(collectionId)
? Arrays.asList("num", "placedOn", "state", "shippedOn")
: null;
}
public List&lt;String&gt; orderStandalone(
final Class&lt;?&gt; collectionType,
final List&lt;String&gt; propertyIds) {
return null; <i class="conum" data-value="3"></i><b>(3)</b>
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>specifies the order in which the <code>TableColumnOrderService</code> implementations are called.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>represents the collection that this service can advise upon</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>provides no advice</td>
</tr>
</table>
</div>
</div>
</div>
</article>
<aside class="article-aside toc hide-for-print" role="navigation">
<p class="toc-title">On this page</p>
<div id="article-toc"></div>
</aside>
</main>
</div>
<footer class="footer">
<div class="content">
<div class="copyright">
<p>
Copyright © 2010~2021 The Apache Software Foundation, licensed under the Apache License, v2.0.
<br/>
Apache, the Apache feather logo, Apache Isis, and the Apache Isis project logo are all trademarks of The Apache Software Foundation.
</p>
</div>
<div class="revision">
<p>Revision: 2.0.0-M5.20210523-1458</p>
</div>
</div>
</footer>
<script src="../../../_/js/site.js"></script>
<script async src="../../../_/js/vendor/highlight.js"></script>
<script src="../../../_/js/vendor/jquery-3.4.1.min.js"></script>
<script src="../../../_/js/vendor/jquery-ui-1.12.1.custom.widget-only.min.js"></script>
<script src="../../../_/js/vendor/jquery.tocify.min.js"></script>
<script>
$(function() {
$("#article-toc").tocify( {
showEffect: "slideDown",
hashGenerator: "pretty",
hideEffect: "slideUp",
selectors: "h2, h3",
scrollTo: 120,
smoothScroll: true,
theme: "jqueryui",
highlightOnScroll: true
} );
});
</script>
<script src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
<script>
function focusSearchInput () { document.querySelector('#algolia-search-input').focus() }
var search = docsearch({
appId: '5ISP5TFAEN',
apiKey: '0fc51c28b4ad46e7318e96d4e97fab7c',
indexName: 'isis-apache-org',
inputSelector: '#algolia-search-input',
autocompleteOptions: { hint: false, keyboardShortcuts: ['s'] },
debug: false,
}).autocomplete
search.on('autocomplete:closed', function () { search.autocomplete.setVal() })
focusSearchInput()
window.addEventListener('load', focusSearchInput);
</script>
<!--
docsearch options:
https://docsearch.algolia.com/docs/behavior/
-->
<!--
https://www.algolia.com/doc/api-reference/api-parameters/
algoliaOptions: { hitsPerPage: 6 },
-->
</body>
</html>