Quantcast
Channel: Oracle Bloggers
Viewing all articles
Browse latest Browse all 19780

JET Composite Components XVII - Beware the IDs

$
0
0

Introduction

When developing Composite Components, we need to be careful about our element IDs, particularly when multiple instances of the component will be used on the same page. This article explores one such use case in relation to labels and input fields in form style layouts

Background

In the last article I discussed how you can use a Composite Component for input within the context of an oj-form layout. This article looks at forms in a slightly different way, specifically implementing a form layout within a Composite Component.

What's the Problem?

On the face of it there is no problem here. You can build a Composite Component that lays out a series of fields using standard JET form layout styles, for example, this might be the view for my component:

<div class="oj-form-layout">
    <div class="oj-form oj-sm-odd-cols-12 oj-md-odd-cols-4 oj-md-labels-inline">
      <div class="oj-flex">
        <div class="oj-flex-item">
          <label for="name">Name:</label>
        </div>
        <div class="oj-flex-item">
          <input id="name" data-bind="ojComponent: {component: 'ojInputText',
                                                                value:name,
                                                                required: true}">
        </div>
      </div>
      <div class="oj-flex">
        <div class="oj-flex-item">
          <label for="type">Type:</label>
        </div>
      <div class="oj-flex-item">
        <select id="type" data-bind="ojComponent: {component: 'ojSelect',
                                                   value: type,
                                                   required:true}">
          <option value="string">string</option>
          <option value="boolean">boolean</option>
          <option value="number">number</option>
          <option value="object">object</option>
          <option value="array">array</option>
          <option value="function">function</option>
        </select>
      </div>
    </div>
  </div>
</div>
    
That all looks OK right? - we have the label tag associated with the relevant input component using the matched ids referenced by the for attribute of the label to the id attribute of the matching input.

Indeed this example with work perfectly when there is only one instance of the CCA in the view. If, however, you have two instances running at once, you'll find that the layout of one of them is all messed up. In the above case, this would only be noticeable on the ojSelect field, its label would loose all styling and not show the required indicator.

What's going on?

Well, as you might have guessed, the problem here is down to the ids that we are referencing to manage the label to input relationship. If I have two instances of this CCA on the same screen (even if one is hidden in a dialog) I now have two fields with the id of name and two with the id of type. As you will be aware. Multiple DOM elements with the same ID are going to cause some degree of problem and in this case it manifests as a broken layout.

The Fix

Once you appreciate the risk for elements with an assigned id, the pattern that you can use to address it is very simple and should be used as a matter of course within your CCAs, even if you think that there will only be one instance.

Step 1 - Get an Instance Identifier

Recall from the article on lifecycle (No. VII in this series, linked below), that the context object that is passed into our CCA constructor includes an attribute called unique. This use-case is exactly what it is for.

In your CCA constructor, just store the value of unique onto a convenient attribute on the component model. I always use unq thus:

    self.unq = context.unique;
    

Step 2 - Generate IDs and FOR attributes

Now that we have the unq variable we can re-write the problem view using the attr:{} knockout binding, thus:

<div class="oj-form-layout">
      <div class="oj-form oj-sm-odd-cols-12 oj-md-odd-cols-4 oj-md-labels-inline">
        <div class="oj-flex">
          <div class="oj-flex-item">
            <label data-bind="attr:{for:'name'+unq}">Name:</label>
          </div>
          <div class="oj-flex-item">
            <input data-bind="attr:{id:'name'+unq},
                       ojComponent: {component: 'ojInputText',
                                              value:name,
                                              required: true}">
          </div>
        </div>
        <div class="oj-flex">
          <div class="oj-flex-item">
            <label data-bind="attr:{for:'type'+unq}">Type:</label>
          </div>
        <div class="oj-flex-item">
          <select data-bind="attr:{id:'type'+unq},
                                ojComponent: {component: 'ojSelect',
                                                     value: type,
                                                     required:true}">
            <option value="string">string</option>
            <option value="boolean">boolean</option>
            <option value="number">number</option>
            <option value="object">object</option>
            <option value="array">array</option>
            <option value="function">function</option>
          </select>
        </div>
      </div>
    </div>
  </div>
      
With this minor change to the code, each instance of the CCA will now have a unique id = for pair generated. One point to note, however, is that this unique id may change from use to use of the same CCA instance. So if your CCA viewModel also wants to manipulate its own DOM or attach listeners based on a specific id, then again you should use the context.unique value to ensure that you get the correct element. You overall goal, however, should be to assign explicit ids to as few a number of elements that you can get away with. Selecting by class may be a better strategy for many DOM manipulation cases.


CCA Series Index

  1. Introduction
  2. Your First Composite Component - A Tutorial
  3. Composite Conventions and Standards
  4. Attributes, Properties and Data
  5. Events
  6. Methods
  7. The Lifecycle
  8. Slotting Part 1
  9. Slotting Part 2
  10. Custom Property Parsing
  11. Metadata Extensibility
  12. Advanced Loader Scripts
  13. Deferred UI Loading
  14. Using ojModule in CCAs
  15. Language Support
  16. CCAs in Form Layouts
  17. Element IDs in CCA

Viewing all articles
Browse latest Browse all 19780


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>