UxMedia

What is UxMedia?

uxBuilder:UxMedia is a type of Px media, new in Niagara 4.10. It marks a Px page as being fully supported, natively, in the browser, using only HTML5 and JavaScript.

UxMedia differs from Hx in that Hx builds out the UI on the station using bajaui first, then using the Hx APIs, converts that UI to HTML and JavaScript strings to send to the browser. UxMedia is built completely upon bajaux, using BajaScript for data bindings. The entire UI is built out and rendered fully in the browser, not on the station.

Why UxMedia?

There were a number of motivations for creating UxMedia.

  1. Performance. UxMedia takes the hard work of rendering and layout, removes it from the JACE, and shifts it to the browser. JACEs are resource-constrained, so a device that is otherwise busy performing the work of communicating with and controlling physical devices may not have enough resources available to render a UI without encountering severe slowdowns. By allowing the user’s own device to do the rendering work, it relieves some of the burden on the JACE itself.
  2. Developer experience. By using the rich set of open-source tools available for JavaScript development, developing widgets for UxMedia is faster, easier, and provides immediate feedback. Using grunt-niagara, a rapid TDD cycle can be implemented, allowing for a comprehensive test suite with a high confidence in quality. Testing out changes to a UxMedia widget does not require a complete module rebuild and a station restart - just hit refresh in the browser to try out new behavior.
  3. User experience. The first iteration of UxMedia attempts to retain the current behavior of the most commonly supported bajaui widgets. Moving forward, new widgets can be developed using current browser features and JavaScript APIs, allowing for a richer, smoother user experience.
  4. Future growth. A UI that is native to HTML5 and JavaScript is more flexible and adaptable to future cloud-based solutions.

Should I choose UxMedia?

Consider the following before deciding whether to use UxMedia.

How can I use UxMedia as a Px page designer?

Simply change the target media of your Px graphic to uxBuilder:UxMedia. Any widgets that do not have full bajaux implementations available will trigger a validation warning.

Audition Mode

In the HTML5 Hx Profile when you have the Web Profile option for ‘Enable View Selection’ turned on, you will see an additional Media Settings Command in the toolbar when viewing a Px graphic. This allows an easy way to try out your existing Px graphics in Audition Mode to see how well they behave in UxMedia.

As an alternative, you can set the system property niagara.preferUxMedia=true, which will display all Px pages in UxMedia in the browser, regardless of audition or media settings.

You can also hide the additional Media Settings command for all users by adding the system property niagara.profile.hideMediaCommand=true.

Opting in to UxMedia

For the first release, we are not making UxMedia the default. This is because it is still under Development status, and not all widgets and bindings are supported at first. But if you find it meets your needs and you would like to enable it across the board, we have provided a migration tool in the pxEditor palette, called PxMediaMigration.

This migration tool will allow you convert some or all of your Px graphics to UxMedia. Full documentation can be viewed here.

Supported Widgets

This list is current as of September 29, 2020. It may not be comprehensive: widgets not in this list may also be supported in UxMedia. Test your pages in Audition Mode to make sure.

Deep Dive: How can I use UxMedia as a programmer?

Here is a quick listing of the different APIs required to develop for UxMedia. Some of these have been in use since the very first release of Niagara 4, while others are brand new in Niagara 4.10 specifically to support UxMedia. Please note that the new APIs are Development status and are subject to change.

bajaux

All UxMedia widgets are implemented with bajaux. Although bajaux itself has had only minimal changes Niagara 4.10, one new API may be of interest to you. bajaux/spandrel allows for easy declarations of entire trees of widgets and HTML elements, which will be used quite often in UxMedia. Please see the bajaux documentation for full details. If you decide spandrel is not right for you, your UxMedia widgets can be implemented using the same bajaux API as before.

BajaScript

In UxMedia, data bindings and ORD resolutions are performed using BajaScript. Most of this should work out of the box, but if you have any custom Simple or Complex Types you Px graphics will need, you may want to consider using Type Extensions for the best experience. Please see the BajaScript documentation for full details.

BIJavaScriptWidget

The same BIJavaScript agent registration mechanism used by regular bajaux views is still at play here. But for use in UxMedia, your classes may also need to implement BIJavaScriptWidget from the web module. This will mark your bajaux class as a direct reimplementation of an existing bajaui widget.

For instance, BUxButton implements BIJavaScriptWidget and is an agent on BButton. This marks BUxButton as a direct reimplementation to be swapped in whenever an instance of BButton is found in a Px graphic.

When implementing BIJavaScriptWidget, please take a look at the validation callback methods and implement them as needed. This will ensure that instances of your widget will correctly validate against UxMedia.

If you already have a Widget that implements BIJavaScript/BIFormFactor and is added to your Px page as a workbench:WebWidget, it will be supported as-is. There is no need to implement BIJavaScriptWidget in this case.

Binding

If you have implemented any custom Bindings, you will need to provide BajaScript-based analogs for use in UxMedia. These will need to be Type Extensions that extend the class nmodule/bajaui/rc/baja/binding/Binding.

Converter

Same for any custom Converters you may have implemented. These will need to be Type Extensions that extend the class nmodule/converter/rc/Converter.

UxModel

UxModel is available from the RequireJS module bajaux/model/UxModel.

Think of a UxModel as an analog of raw Px data, just transcribed to JSON form and given an API. It describes a tree of Widgets and Bindings, but does not actually instantiate them. Instead, it is used by Widgets whose child Widgets are user-configured in the Px data itself.

An example of a widget whose child Widgets are user-configured is CanvasPane. It has no inherent child widgets of its own. But if a user drags a Label from the palette onto the CanvasPane, it gets a new child widget. A Label, however, cannot have any user-configured child widgets of its own: if you drag something onto a Label, nothing happens. The Label is self-contained.

Therefore, when implementing UxMedia analogs of these widgets, our CanvasPane implementation will need to know about UxModel, because that’s what lets CanvasPane know what its children are. A UxMedia Label doesn’t need to care about UxModel because it has no children.

This will be much clearer if we look at a concrete example in the original Px and compare it to how it works in UxMedia.

Example: Original Px

For this example, imagine we created a .px file, set the root widget to a CanvasPane, and added a Label with the text “Hello.” We now have a .px file that looks like this simplified example:

<!-- myFile.px -->
<CanvasPane>
  <Label text="Hello"/>
</CanvasPane>

I open this file in Workbench and sure enough, I see a CanvasPane with a Label on it that says Hello. How does this process work?

First, the .px file is parsed into a tree of bajaui widgets. A dedicated Px parser looks at the .px file and knows to execute code that looks something like this:

BLabel label = new BLabel();
label.setText("hello");
BCanvasPane canvasPane = new BCanvasPane();
canvasPane.add("Label1", label);

We’ve taken the .px file and parsed it out to a tree of BWidgets. The Px view itself will ask the CanvasPane to paint itself. How does the CanvasPane know to paint that child Label? Because it uses the Niagara component model, and the child Label shows up as a child slot!

// in BCanvasPane.java
public void paint(Graphics g) {
  BWidget[] kids = getProperties(BWidget.TYPE);
  for (BWidget kid : kids) {
    kid.paint(g);
  }
} 
Example: UxModel

When you open this page in UxMedia, under the covers, it’s going to make a request up to the station to get the contents of the .px page. The response will have all the same information as the .px file, but it will be restructured a bit to use JSON format. It will look something like the below - compare and contrast with the Px data in its original XML shown above.

{
  type: CanvasPane,
  kids: [
    { 
      type: Label,
      properties: { text: 'Hello' }
    }
  ]
}

Now in the browser, that blob of JSON will be converted into a UxModel instance. It’s all the same data, but has an API around it for ease of use. See the imaginary example below:

return retrieveFromStation('myFile.px')
  .then((jsonBlob) => UxModel.make(jsonBlob))
  .then((uxModel) => {
    console.log(uxModel.getType()); // CanvasPane
    const [ labelModel ] = uxModel.getKids();
    console.log(labelModel.getType()); // Label
    console.log(labelModel.getProperties().text); // Hello
  });

Observe: the UxModel is the same data as the JSON blob, which is the same data as the .px file. It’s not an actual tree of widgets - it’s just data on how to create a tree of Widgets.

When the Px widget has child widgets, then your corresponding bajaux Widget will receive a UxModel instance that lets you know what those child widgets are. Your Widget can then instantiate them into child elements as it sees fit. As you might imagine, this is crucial behavior for Panes, because child widgets is all they are about! For other kinds of Widgets, not so much.

Here’s how our simplified CanvasPane implementation might look:

class CanvasPane extends spandrel((uxModel) => {
  return uxModel.getKids().map((kidModel) => {
    return kidModel.toSpandrel('<div class="canvasPane-child"/>');
  });
}) {};

Here’s a non-spandrel example. (Note that if the child widgets changed, or if one were added or removed, that additional work will have to be done: either wipe all the existing children and rebuild, or write code to diff the new widgets against the old. spandrel, if used, would do all this work for you.)

class CanvasPane extends Widget {
  doLoad(uxModel) {
    return Promise.all(uxModel.getKids().map((kidModel) => {
      return fe.buildFor({
        dom: $('<div class="canvasPane-child"/>').appendTo(this.jq()),
        type: kidModel.getType(),
        properties: kidModel.getProperties()
        // readonly, enabled, formFactor...
      });
    }));
  }
}

Put it side by side:

If your UxMedia widget does not need to make use of a UxModel, only remember that it will still receive a UxModel instance when its load() method is called - you’re just free to ignore it if it’s not needed.

Styling with CSS

If you spin up a UxMedia page and inspect the DOM, you’ll see a number of CSS classes in use. This section will describe some of the CSS conventions we are following in UxMedia.

When you see a CSS class that looks like ux-Widget - for instance, ux-Label or ux-TabbedPane - that CSS class identifies an element in which a UxMedia widget lives. You can call Widget.in() on these elements, and a Label, TabbedPane, etc. will come out. You can rely on this behavior.

When you see a class that looks like ux-Widget-style - for instance, ux-Label-content or ux-TabbedPane-tabs - it indicates a child element that can be styled externally. If you have a custom Niagara theme, and you wish to apply themed styles to UxMedia widgets, you can target these classes for styling. This behavior is mostly safe to use, in that we will try to minimize changes to these classes. Until UxMedia is out of Development status, there may still be some changes from version to version. Note that the first letter after ux- is uppercase.

When you see a class that looks like -t-Widget-style - for instance, -t-Label-text or -t-TabbedPane-content - it indicates style rules considered to be “private”. (The -t- stands for Tridium.). They are always subject to change, so you should not rely on these classes remaining consistent from version to version.

Classes that are not specific to UxMedia are described below:

When you see a class that looks like ux-some-rule - for instance, ux-btn or ux-root - those classes are typically the standard classes applied for theming purposes, not just for UxMedia but across Niagara’s entire bajaux UI. Note that the first letter after ux- is lowercase. You can visit a test page in a running station by visiting /module/web/rc/theme/test.html to learn more about these classes. You may see a few other classes that are not covered in the theme which are used for JavaScript rather than styling.

When you see a class that begins with bajaux-, those are native to bajaux itself and are not typically used for styling purposes. You can find these classes described in the Widget documentation.

Performance Notes

As stated before, performance is one of the primary motivations for creating UxMedia. We are eager to hear about your real-world use cases and whether you see performance gains, losses, or no changes at all. If you give an audition to UxMedia, please consider reaching out through your tech support channel, sales rep, or the Niagara Community Forums and let us know your experiences.

We want to make your experience with UxMedia the very best it can be.

Thanks for reading!