X

Technical Articles relating to Oracle Development Tools and Frameworks

  • JET
    February 22, 2017

JET Custom Components XVII - Beware the IDs

Duncan Mills
Architect

Last Updated Jan 2020 for JET 8

Introduction

When developing custom JET 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  although it applies in any scenario where known element IDs are required within a 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">
        <oj-label for="name">Name:</oj-label>
      </div>
      <div class="oj-flex-item">
        <oj-input-text id="name" 
                       value="{{name}}"
                       required="true">
        </oj-input-text>
      </div>
    </div>
  </div>
</div>

That all looks OK right? - we have the label tag associated with the relevant input component using the matched reference by the for attribute of the label to the ID attribute of the input.

Indeed this example with work perfectly when there is only one instance of the component in the view. If, however, you have two instances running at once you will now have duplicate element IDs within the DOM which can lead to all sorts of problems, some of them visible e.g. the component might not render as expected, or some more invisible, e.g. the wrong handler is called when an event is raised.

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 components, even if you think that there will only be one instance because there is still a danger of collision with something else on the page with the same ID.

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 uniqueId. This use-case is exactly what it is for. You can cache this value for programatic use. In your component constructor, just store the value of uniqueid onto a convenient attribute on the component model. I always use self.uniqueId thus:

self.uniqueId = context.uniqueId;

However, most of the time you will actually need this value in the HTML template, and the framework provides a pre-canned object for you in the form of $uniqueId.  This can be used within your template where-ever you need to create a unique ID or ID reference.

Step 2 - Generate IDs and FOR attributes

Now that we know we have the $uniqueId variable we can re-write the problem view using an expression and the :id binding (note the leading colon, that allows the use of an expression in this stanadrd HTMLElement attribute!), 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">
        <oj-label for="[['name'+$uniqueId]]">Name:</oj-label>
      </div>
      <div class="oj-flex-item">
        <oj-input-text :id="[['name'+$uniqueId]]" 
                       value="{{name}}"
                       required="true">
        </oj-input-text>
      </div>
    </div>
  </div>
</div>>

You'll see that the for property of oj-label is also set with the same expression. 

With this minor change to the code, each instance of the component 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 component. So if your component viewModel also wants to manipulate its own DOM or attach listeners based on a specific ID in code, then again you should use that stored uniqueId 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.


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.