X

Technical Articles relating to Oracle Development Tools and Frameworks

  • November 29, 2018

JET Custom Web Components in Oracle Visual Builder

Duncan Mills
Architect

 

In this article, I'll be discussing various topics relating to how JET Custom Web Components can be optimized for use in Visual Builder Cloud Service.
First of all - just to be clear - at one level, there is nothing to do. JET custom Web Components will "just work" out of the box with Visual Builder. This should come as no great surprise, after all, the user interface that you create in Visual Builder is just a JET UI. You can get away without doing any specific work for Visual Builder at all, however, there are several areas in which you can optimize the experience of consuming your components and this is what I'll be concentrating on in this article.

I'm going to divide things up into two sections.  First of all some design considerations for the components from a functional point of view, and then a review of the metadata for your components and how that can effect Visual Builder design time 

Good Practice in Component Design for Visual Builder

So let's look at the couple of things that you need to consider when creating components for use by developers using Visual Builder.

How it Looks at Design Time 

When using the visual design view of Visual Builder, the page that you are creating is actually being run for you.  This is  so that you get a reasonable interpretation of how the final runtime page will appear with real data.  However, this can present a challenge for you as when your custom component is first dropped into the page. This is because when first dropped it will be dropped as a bare component tag and not configured in any way. A common result of this is that the component will often seem to "disappear" and be impossible to select on the visual canvas (although it can be selected in the structural navigator). Why is this? Well it's a problem with the component itself. Often when a component is instanciated without setting any of it's properties it will actually render no UI at all and it's this that makes it disappear, effectively it may collapse to a 0x0 px element. Various solutions are possible to prevent this happening and make it easier for the user to find the component once it's in the view. 

  1. Give the component a minimum height and width.  In the component CSS file define some minimum sizing just so it can't collapse to a zero point.
  2. If the component requires some information before it can even attempt to draw, then have it output some useful message as a placeholder that can disappear once configuration is complete.  For example, here's my Google Maps integration component when it is first dropped onto the canvas. This component needs a Google API key before it can operate, so whilst I'm waiting for the developer to provide that I at least tell them so:

Unconfigured component display at DT
Of course as soon as the required information is supplied, the component then uses some sensible defaults to show something to the user which they can then further configure:

You'll notice here that the property inspector is showing some default values for things like the map type and the zoom level, although the default map-centerpoint is something the component defaults internally until the component user overrides it.  This simple act of thinking about the consuming developers first encounter with your component is so important! 

How the Component Reacts to Change

Within the Visual Builder environment it's really important that your components are correctly listening for external changes to property values as they happen. It's not enough to just process the configuration properties in the constructor of your component and be done with it. You must also be watching for asynchronous changes to those properties once the component is instanciated. 
The reason for caring about this is that when properties are bound to Visual Builder variables, the population of those variables might well be asynchronous and not be complete until after your component has been started. If you don't listen for ongoing changes to the property, then your component might not be seeing the correct configuration values.  This is a really common issue for rookie component developers - don't get caught out! 
Fortunately JET makes it really easy to handle this situation, Custom Components internally support a propertyChanged() lifecycle method which will be automatically called whenever a bound variable is updated (either internally or externally to the component).  So you should always provide an implementation of this method to update your component as those changes occur. 

Fully Formed metadata

Now that I've looked at the key design mistakes to avoid, let's look at how you can improve the user experience of your components in Visual Builder design time; through the use of well formed metadata.  JET Custom Web Components have a comprehensive metadata definition that you can find in the JSDoc (here). Visual Builder design time pays particular attention to this information, and so the better the definition you provide, the better the end user experience will be. 
Let's look at the key metadata attributes as used by the design time:

"displayName"

The displayName attribute can appear in multiple places in your metadata. At the top level it is applied to the component as a whole but can also be defined for individual properties and event as well. It is basically used to provide user-readable names for use in design time. 
So for example, if the displayName is set at the component level then that will be used to label the component in the component palette, e.g. "Google Map" instead of the full tag name (oj-sample-google-map) which takes up more space and is harder to read.
In the defintion:
{
  "name": "oj-sample-google-map",
  "displayName": "Google Maps",
  "version":"1.0.0",
  ...
}

The images below show what the component palette entry would look like both with and then without the displayName being set:

With displayName                                                   Without
  
We can also see how displayName is used for the Property Inspector when provided for individual properties. Again it just provides a nicer (and translatable!) label for the Property Inspector Field. 
So for this property defintion:
"zoom": {
  "type": "number",
  "displayName": "Zoom level",
  ...
}

We get the nicer "Zoom level" label on the field.

Longer, camel cased property names really benefit from this capability.

"description"

Just like displayName, the description attribute is present at various levels of the metadata and using it you can provide a additional information about the component as a whole or just a single property.  In either case you'll see a question mark icon displayed at design time and when you hover over that you'll get the description information displayed to help the user of the component.

Component Level Property - "icon"

The icon attribute only exists at the top level of the component defintion, and not surprisingly allows you to provide a custom image to display in the component palette for the component. This attribute actually provides three sub-attributes to allow you to set both the base, selected and hover-over state of the icon. (Note that currently the selected state is not used or needed so just define the base and the hover version) e.g. 
{
    "name": "oj-sample-google-map",
    "displayName": "Google Maps",
    "icon": {
        "iconPath": "extension/images/map.png",
        "hoverIconPath":"extension/images/map_hov.png"
    },

You saw the result of this in the earlier image:

Images should be in either png or svg format and if png should be sized to no greater than 20x20px to fit correctly (16x16 is ideal). SVG images will be scaled for you. The default color for the icon in normal state should be #8a8d8f and white (#ffffff) in the hover state if you supply that.

Property Level Attribute - "value"

The property level attribute value is used at runtime to provide a default value for the specified property, but it is also used at design time in Visual Builder and is displayed as a place-holder in the property inspector field so that the user can see what the default would be.  We saw this already in the screenshot of the zoom property PI. As it's a placeholder value it is displayed as a lighter grey colour and the user can of course overtype it:

Property Level Attribute - "required"

The required attribute in the property metadata is honored in the form of an indicator (*) on the field - just a hint to the user that this is a property that really must be supplied. Here we see it on the apiKey property of the map component:

As you can see, the user will also get a gentle reminder if they still forget to provide that property!

Property Level Attributes - "type" and "format"

The type attribute of a Custom Web Component property tells the runtime how to handle the conversion of the string value found in the HTML tag into the datatype that the component actually asks for.  However, just like value and required attributes, this information is also used at design time to add some extra help for the user.  Different types will be serviced by different sorts of input fields - for example, we've seen the "Zoom level" Property Inspector field is shown as a number input with a spinner, not a plain text field control.  Design time actually combines the type attribute with a second attribute called format which can further refine what kind of control to display. 
The supported values for format are:
For when type=number

  • double    floating point number with double precision
  • float    floating point number with single precision
  • int32    signed 32-bit integer
  • int64    signed 64-bit integer

For when type=string

  • color    CSS color value
  • date    date in RFC 3339 format, using the "full-date" profile
  • date-time    date-time in RFC 3339 format, using the "date-time" profile
  • time    time in RFC 3339 format, using the "full-time" profile

In these cases specialised input fields such as date or colour pickers will be presented in the property inspector.

Property Level Attributes - "minimum" and "maximum"

When dealing specifically with a type=number, the minimum and maximum attributes will come into play to restrict the values that the user can enter into a field to those range constraints.  Note that this will prevent the property inspector from being used to enter out-of range values, however, the user can still edit the tag in the source code to breach the range.  therefore your component will still need to do some runtime checking to enforce the defined rule. 

Property Level Attribute - "enumValues"

Like number properties, string properties some times have a "range". In the case of stings this is specifically a set of valid values defined using the enumValues attribute.  When Visual Builder design time sees a string attribute with an enumValues then, rather than just displaying a text input field, it will instead display a select-one choice drop-down in the property inspector. In doing so, it helps the consuming developer by listing the values up front in a simple to use form, meaning that the developer does not need to refer back to the documentation.

Property Level Attribute - "propertyEditorValues"

Just like the use of displayName to make the labels for properties more readable, sometimes you don't want the actual enumeration values to be what you display in the enumValues drop-down.  If you need something more readable then you define a propertyEditorValues attribute for the property and for each enumeration optioin you can provide extra metadata for the displayName and description. Here's an example from the mapType property:
  "mapType": {
    "description": "Display style of the map",
      "type": "string",
      "writeback": true,
      "enumValues": [
          "roadmap",
          "hybrid",
          "satellite",
          "terrain"
      ],
      "propertyEditorValues": {
          "roadmap": {
            "displayName": "Roadmap",
              "description": "Displays the basic map style concentrating on road / transport links."
          },
          "hybrid": {
              "displayName": "Hybrid",
              "description": "Displays a mix of roadmap and satallite views."
                },
                "satellite": {
                    "displayName": "Satellite",
                    "description": "Displays Google Earth satellite images."
                },
                "terrain": {
                    "displayName": "Terrain",
                    "description": "Displays a physical map with roadmap overlay."
                }
            },
            "value": "roadmap",
            "propertyGroup": "common"
        } 

Again notice how displayName is used to provide an alternate label in the drop down (in this case capitalized versions of the possible values). Here's the result:

Property Level Attribute - "propertyGroup"

Finally, each property can define which propertyGroup it is a part of.  PropertyGroup is a logical collection of properties and is used as a hint by Visual Builder design time to decide on which Property Inspector tab to display the property on. All properties are visible on the "All" tab in the Property inspector, but if your compoennt has an important property that needs to be set most of the time then you'll want it to appear on the General tab of the property inspector. To do this, you set the propertyGroup attribute to "common". Likewise you may set the propertyGroup to "data" to make the property appear on the Data tab. If we look again at the property inspector for the Map component:

You can see how I've just put the most important properties, those that are set nearly all the time, on the General tab. The idea here is to help the user to understand what parts of the component API are the most important to set, so consider carefully which properties you prioritze in this way.

Summary

As you can see, with relatively little effort you can really improve a component consumers experience of using your component. In general, everything that I've listed here makes sense to define even if you are not particularly targeting the Visual Builder platform. This is simply because in doing so you are defining the API of your component more precisely which can only be a good thing!


JET Custom Component Series

If you've just arrived at Custom JET Components and would like to learn more, then you can access the whole series of articles on the topic from the Custom JET Component Architecture Learning Path

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha