Interface ComponentXML

public interface ComponentXML

Component XML

Component XML is an XML format for declaring Smart GWT components and screen definitions.
Available with Smart GWT Pro and above, Component XML is the same format used by Visual
Builder to save screens.

By allowing you to keep layout information and property settings in an XML format, Component
XML enables non-developers to build and maintain portions of your application, either by
editing screens within Visual Builder or by directly editing the XML itself.

Unlike the similar GWT "UIBinder" technology, Component XML does not require a compilation
step. XML screen definitions can be generated on the fly, modified at runtime, stored in a
database, and in all other ways treated as a dynamic resource. See the section "Dynamic
Component XML" for details.

Basic Usage

To create a Smart GWT component in XML code, you create a tag with the component's class
name. You can set that component's properties either as tag attributes:

<Button title="Click me" width="200" />

or in nested tags:

<Button>
<title>Click me</title>
<width>200</width>
</Button>

To set a property that is an Array of simple types (like int, or String), repeat tags like
so (for DynamicForm.colWidths):

Loading screens stored in Component XML

Save your Component XML as a file called screenName.ui.xml under
webroot/shared/ui/. Placing your .ui.xml file in this directory makes it visible to
the system; the location of this directory can be configured in server.properties
by setting
the project.ui property. screenName can be any valid identifier (no spaces,
dashes or periods - underscores OK).

If you have multiple top-level tags (eg, your code is similar to the example above under
"Referring to previousy defined components") use <isomorphicXML> as a top-level
container tag - this has no impact on processing and is just an idiom to make your file valid
XML, since XML does not allow multiple top-level tags in a document.

Component XML screens are then loaded using the ScreenLoaderServlet. The default SDK comes
with this servlet already registered at
projectBase/sc/screenLoader. If you've modified web.xml
or only included some of the default servlets, you may need to add it now - see the
Installation Instructions.

To create an application that consists of just the imported mockup, just add a
<script src> tag pointing to the ScreenLoader servlet and referring to the
screenName you used when you saved your file.
For example, add the following to your bootstrap .html file:

<script src="sc/screenLoader?screenName=screenName"></script>

If you want to load screens dynamically, or if you want to load more than one screen, use
RPCManager.loadScreen(). See the section
on "Multiple screens and global IDs"
below.

Event Handlers & Scripting loaded components

You can retrieve the components in your loaded screen in order to add event handlers to
them, call APIs on them, place them into layouts you programmatically create, and in general
add dynamic behavior. Retrieve the components via the Canvas.getById() API
(note, when working with multiple screens, be sure to see the upcoming section about managing
global IDs).

You can then add event handlers normally. For example, say there is a ListGrid with ID
"mainGrid" and a DynamicForm with ID "editForm" in the same screen, and you want to populate
the form with whatever record is clicked on in the grid:

You can also add a loaded screen to an existing layout container. For example, perhaps you've
already written parts of the application via normal coding techniques, and now you want to take
a screen defined in Component XML and place it in a particular Layout you've already created
("existingLayout" below) - just use Layout.addMember() as usual:

existingLayout.addMember(Canvas.getById("componentId"));

Component XML files can also refer to components you have created programmatically, and
incorporate
them into layouts. For example, if you have created a ListGrid component with ID "theGrid",
you could
refer to that grid using a <Canvas ref=""/> tag, which can be used anywhere
a
Canvas is expected. For example:

Note that this approach requires that the referenced component has been created beforeloadScreen is called.

Component XML and global IDs

A Component XML screen created in Visual Builder or via the
Balsamiq importer will assign global IDs to all
components
generated from your mockup so that you can retrieve them by ID to add event handlers and
call APIs. However if you build an application out of multiple screens built at different
times, these IDs can collide, which will cause components to overwrite each other as they
each try to use the same ID.

To solve this, the RPCManager.loadScreen() API will ignore global IDs on loaded
components, assigning them sequential generated IDs instead (which will never collide).
Instead of using global IDs, the callback for loadScreen() will automatically provide
you with the outermost component of a loaded screen, and that outermost component will
provide access to other components by their original IDs via Canvas.getByLocalId().

This allows you to add loaded screens to existing layouts, attach event handlers and take
other programmatic actions, all without ever establishing global IDs.

Loading multiple screens

A typical application that uses screens stored in Component XML will have several such
screens, or in some cases, hundreds or thousands. RPCManager.cacheScreens() can be
used to load a set of screen definitions without actually creating any UI components - a
subsequent call to RPCManager.createScreen() is used to actually create the screen
when needed. These two APIs provide the same global ID management facilities as
loadScreen().

As discussed in the Smart GWT Architecture
overview, screen
definitions are typically very small, and should be loaded entirely up front or in very
large batches. Further, screen definitions have essentially negligible runtime
overhead until the screen is actually created.

Therefore, use the following best practices for screen loading, even if you have very few or
only one screen defined in Component XML:

for applications with very very large numbers of screens where loading all screen
definitions up front creates a very large download, consider multiple calls to
cacheScreens(), loading sets of screens that are likely to be used
together.

Dynamic Component XML

Components can be dynamically provided on the server side by using the API defined in
ScreenLoaderServlet.addDynamicScreenGenerator()
which allows adding DynamicScreenGenerators to the system for providing the .ui.xml files on
the fly:

Whenever the system needs a screen in future, it will first call the registered
DynamicScreenGenerator's
getScreen(String) method for providing the given screen; Only if all queried
DynamicScreenGenerators returns null, will proceed to use the normal system for
obtaining
the screen instances.

NOTE:

If this API is used, DynamicScreenGenerator will be called for every screen that
the framework needs. Instead of this, the API contains alternative methods which will allow
adding DynamicScreenGenerators only for a given string prefix or a regular expression.

In the provided example we register a DynamicScreenGenerator which will be called for each
screen
the system tries to load, if it starts with "testDynamicScreenPrefix", except the screen with
id
"testDynamicScreenPrefix3" for which we return null.

While registering a DynamicScreenGenerator is the first choice since is compatible
with ScreenLoaderServlet's ability to load several screens in a single HTTPRequest,
there are two additional ways to load Component XML screens - you can create a .jsp that
uses the JSP tags that come with the SDK:

However these two approaches will allow to only load one screen at a time.
The JSP code above and the programmatic call to XML.toJS() both return a JavaScript code,
which is the response that RPCManager.loadScreen() expects. The
XML.toJS() API can be easily combined with
direct use of the server-side
DataSource API to build
a version of the ScreenLoaderServlet that can retrieve Component XML from a database
or any Java API.

For static Component XML screens (cannot be changed at runtime), you can optionally run the
XML.toJS() process as a build step to save a small amount of runtime overhead in XML to JS
translation. Use RPCManager.loadScreen()
to load the resulting JavaScript by
overriding the RPCRequest.actionURL to
point to the generated JavaScript file.
Note that the overhead is minor enough that this is not worth doing unless you have a very
large deployment and a very large number of static Component XML files.

Troubleshooting

XML parsing errors, which happen when XML is not well-formed and would be rejected by any
standard XML parser, are reported in the server-side log, and, when possible, also in the
client-side log within the "Results" tab of the Developer Console.

If you are loading a screen via the RPCManager.loadScreen() API, you can see the
response from the server in the RPC tab of the Developer Console - this will show you issues
such as a misplaced ScreenLoaderServlet (HTTP response code will be 404 - Not Found) or
responses that contain server exception details instead of the expected JavaScript response.

You can also use the "Eval XML" section in the "Results" tab of the Developer Console to
interactively experiment with Component XML ("Eval XML" button) and as a means of seeing the
generated JavaScript ("Show JS" button).

Localization / Internationalization

Component XML files support embedding references to messages loaded from ResourceBundles via
the same JSTL-like <fmt> syntax as is used for DataSource .ds.xml files. See
DataSource localization for details.

Custom Components

If you define a new component class com.mycompany.MyListGrid which is a subclass
of the
built-in component ListGrid, and you register your class for reflection,
you can create it in XML as shown below:

<ListGrid constructor="com.mycompany.MyListGrid" width="500"/>

By using the <ListGrid> tag you advertise that properties should be interpreted
as ListGrid properties. By specifying constructor
you tell SmartGWT what class to create.

Custom Properties

Your custom component (e.g. com.mycompany.MyListGrid) may have additional
properties which are not present in the standard superclass (e.g. ListGrid).
You can set such properties in XML as if they were pre-defined properties:

<ListGrid constructor="com.mycompany.MyListGrid" myProperty="false"/>

In this case, the BeanFactory code
will ultimately call MyListGrid.setMyProperty(false); in order
to set the property. Since BeanFactory knows that the property
takes a boolean, it will automatically convert the string value "false" to a
boolean, using the type conversions described below.

Instead of relying on the automatic type conversions, you can force a
property to be interpreted as a given type by using the "xsi:type"
attribute:

The same notation works when you want to declare that an entire subobject has a given type.
For example, this would cause the custom property "myListGrid" to have a live
ListGrid instance as its value. All of the properties
on the <myListGrid> tag
will be correctly interpreted as ListGrid properties and have the correct types.

Component Schema

Instead of using the constructor and xsi:type attributes for
custom components and custom properties, you can create a ComponentSchema that
describes the custom component. Declaring a component schema allows you to use your
component just like the built-in SmartGWT components, and also allows your component to
be used within VisualBuilder.

Type Conversions

The BeanFactory code
uses a reflection-like mechanism to discern the type which a SmartGWT
property requires, and automatically
converts supplied values to the required type when possible. In cases where
conversion is impossible, an IllegalArgumentException is
thrown.

Where the setter for a property takes a primitive type (boolean,
double, float, int, or long), any "null" value supplied will be converted to
0 (for the numeric types) or false (for boolean). Conversely, if the setter
takes the boxed version of the type (Boolean, Double, etc.), any primitive
value supplied will be auto-boxed. Note that byte, short and char properties
are not currently handled.

Properties which take numeric types will convert other numeric
types, as well as strings, using standard Java APIs (e.g.
Integer.valueOf()). Boolean "true" will be converted to 1, and false to 0.
If the supplied value cannot be converted to the numeric type, an
IllegalArgumentException will be thrown.

Properties which take Enum types will convert from strings using
Enum.valueOf().
However, any dashes ("-") in the string will be converted to underscores, and
the string will be converted to upper-case if necessary. If the string does not match
one of the Enum values, an IllegalArgumentException will be thrown.

Properties which take Array types will convert arrays where the individual
values can be converted to the appropriate type. If a single value is supplied,
it will be wrapped in an array.