X

Technical Articles relating to Oracle Development Tools and Frameworks

  • JET
    February 15, 2017

JET Custom Components XVI - Components in Form Layouts

Duncan Mills
Architect

Introduction

A common use for Composite Components is to create enhanced input components that address specialized use cases. A good example of this might be a specialized input component for entering IP Address formats. With this kind of component you would want to be able to use it everywhere that a normal JET input* type could be used. This article discusses the pattern that you can adopt to make this happen.

What's the Problem?

To illustrate this process I'm going to use an example form from a little application that I'm building. This is a wizard based UI that, guess-what, helps you to create CCAs. Here's an early version of one of the screens:


This form uses a classic oj-form-layout to responsively align the fields:

<div class="oj-form-layout">
  <div class="oj-form oj-sm-odd-cols-12 oj-md-odd-cols-3 oj-md-labels-inline">
    <div class="oj-flex">
      <div class="oj-flex-item">
        <label for="ccaname"
               data-bind="text:res.ccawEditor.nameLabel"/>
      </div>
      <div class="oj-flex-item">
        <input id="ccaname"  placeholder="my-component"
               data-bind="ojComponent: {component: 'ojInputText',
                                            value:workingCopy.name,
                                            required: true,
                                            rootAttributes: {
                                            style:'max-width:30em'}}">
      </div>
    </div>
    <div class="oj-flex">
      <div class="oj-flex-item">
        <label for="ccaversion"
               data-bind="text:res.ccawEditor.versionLabel"/>
      </div>
      <div class="oj-flex-item">
        <input id="ccaversion"
               data-bind="ojComponent: {component: 'ojInputText',
                                        value:workingCopy.version, required: true}">
      </div>
    </div>
    <div class="oj-flex">
      <div class="oj-flex-item">
        <label for="ccajversion"
               data-bind="text:res.ccawEditor.jetVersionLabel"/>
      </div>
      <div class="oj-flex-item">
        <input id="ccajversion"
               data-bind="ojComponent: {component: 'ojInputText',
                                        value:workingCopy.jetVersion, required: true}">
      </div>
    </div>
    <div class="oj-flex">
      <div class="oj-flex-item">
        <label for="ccadesc"
               data-bind="text:res.ccawEditor.descriptionLabel"/>
      </div>
      <div class="oj-flex-item">
        <textarea id="ccadesc" rows="6" style="resize: both;"
                  data-bind="ojComponent: {
                      component: 'ojTextArea',
                      value: workingCopy.description,
                      rootAttributes: {style:'max-width:60%'}}"></textarea>
      </div>
    </div>
  </div>
</div>

In this screen we can see a couple of version related fields. Right now these are simple ojInputText fields but I want to replace them with something a little more intelligent that would include behaviors such as validating the version according to Semantic Versioning rules and allowing range selection. So this seems like a good candidate for a Composite Component!

So the Composite Component (called <ccaw-semvar-input>) is created and dropped into replace the existing version number inputs:

<div class="oj-form-layout">
  <div class="oj-form oj-sm-odd-cols-12 oj-md-odd-cols-3 oj-md-labels-inline">
    <div class="oj-flex">
      <div class="oj-flex-item">
        <label for="ccaname"
               data-bind="text:res.ccawEditor.nameLabel"/>
      </div>
      <div class="oj-flex-item">
        <input id="ccaname"  placeholder="my-component"
               data-bind="ojComponent: {component: 'ojInputText',
                                        value:workingCopy.name,
                                        required: true,
                                        rootAttributes: {
                                        style:'max-width:30em'}}">
      </div>
    </div>
    <div class="oj-flex">
      <div class="oj-flex-item">
        <label for="ccaversion"
               data-bind="text:res.ccawEditor.versionLabel"/>
      </div>
      <div class="oj-flex-item">
        <ccaw-semver-input id="ccaversion" version="{{workingCopy.version}}"
                         placeholder="1.0.0"
                         range-selection="false" required="true"/>
      </div>
    </div>
    <div class="oj-flex">
      <div class="oj-flex-item">
        <label for="ccajversion"
               data-bind="text:res.ccawEditor.jetVersionLabel"/>
      </div>
      <div class="oj-flex-item">
        <ccaw-semver-input id="ccajversion" version="{{workingCopy.jetVersion}}"
                           placeholder="2.3.0"
                           range-selection="true" required="true"/>
      </div>
    </div>
    <div class="oj-flex">
      <div class="oj-flex-item">
        <label for="ccadesc"
               data-bind="text:res.ccawEditor.descriptionLabel"/>
      </div>
      <div class="oj-flex-item">
        <textarea id="ccadesc" rows="6" style="resize: both;"
                  data-bind="ojComponent: {
                      component: 'ojTextArea',
                      value: workingCopy.description,
                      rootAttributes: {style:'max-width:60%'}}"></textarea>
      </div>
    </div>
  </div>
</div>

Unfortunately, if you just do a straight substitution like this, then the result is not what you'd like:

As you can see we have two problems, firstly we've lost the alignment with the other field labels and secondly we've lost the required indicator. Let's look at solving each of those in turn.

Correcting the Alignment

What we're seeing here is not a bug in the framework, it is expected behavior for cases where you are mixing non-JET components into the form layout. The solution is actually outlined within the existing cookbook demo Form Layout - Non Components. All we have to do is to apply the classes oj-label-nocomp oj-label-for-non-control to the label e.g.

<div class="oj-flex">
  <div class="oj-flex-item">
    <label for="ccaversion"
           class="oj-label-nocomp oj-label-for-non-control"
           data-bind="text:res.ccawEditor.versionLabel"/>
  </div>
  <div class="oj-flex-item">
    <ccaw-semver-input id="ccaversion" version="{{workingCopy.version}}"
                     placeholder="1.0.0"
                     range-selection="false" required="true"/>
  </div>
</div>

This will ensure that the labels are now correctly aligned.

Adding the Required Indicator

For the case of using a non-JET component, the framework supplies a class specifically for rendering the *required indicator. This class is called oj-label-required-icon. However, one thing we can't do is this:

<label class="oj-label-nocomp oj-label-for-non-control  oj-label-required-icon" .../>

Doing so would result in this:

As you can see, the required indicator is overriding both the color and font information for the label if it is applied directly. Thus the correct usage is:

<label class="oj-label-nocomp oj-label-for-non-control" 
       style="display: inline-block;" 
       for="ccajversion">
  <span class="oj-label-required-icon oj-component-icon"/> 
  <span data-bind="text:res.ccawEditor.jetVersionLabel"/> 
</label>

Here we have done two things:

  1. Moved the oj-label-required-icon into it's own span to limit its influence and set the oj-component-icon class as well
  2. Set display:inline-block; on the containing label component. This ensures that the required indicator will remain correctly aligned with the label text even when the form layout re-locates the label to above the field in small screens

With these small changes to the label styles the layout and responsiveness of your oj-form-layouts will return and you can mix in your composite components at will. Here's the fixed form:

And when resized to SM screen size to force a label move


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 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