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
So let's look at the couple of things that you need to consider when creating components for use by developers using Visual Builder.
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.
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!
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.
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:
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:
"displayName": "Google Maps",
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:
"displayName": "Zoom level",
We get the nicer "Zoom level" label on the field.
Longer, camel cased property names really benefit from this capability.
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.
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.
"displayName": "Google Maps",
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.
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:
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!
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
For when type=string
In these cases specialised input fields such as date or colour pickers will be presented in the property inspector.
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.
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.
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:
"description": "Display style of the map",
"description": "Displays the basic map style concentrating on road / transport links."
"description": "Displays a mix of roadmap and satallite views."
"description": "Displays Google Earth satellite images."
"description": "Displays a physical map with roadmap overlay."
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:
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.
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!
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