X

Technical Articles relating to Oracle Development Tools and Frameworks

  • July 11, 2018

JET Custom Components XXII - Template Slots

Duncan Mills
Architect

In JET 5.1.0 we introduced a really exciting and useful addition to the component authors toolbox, that of templated-slots. Before I dive into the mechanics though, I'd like to discuss the kind of problems that they are designed to solve:

It's All About Scope

If you have used normal slots, you will be aware that the intent is to allow the component author to leave placeholders in the component view for consumers to drop in their own content.  The typical example of this being something like a toolbar component which supplies a certain set of pre-ordained buttons, but which supports a slot for consumers to add some of their own.  So slot content is supplied by the consumer but "managed" from a layout perspective by the component.

This is all very good, however, there is one restriction that can limit the usefulness of slots and that is the matter of data scoping.  Any components that you put into a slot may well end up as children of the custom component in UI / DOM terms, however, in data binding terms they are peers of the custom component. The slotted components cannot see the data that the CCA is managing internally and are effectively bound to the same parent viewModel as the custom component is.  This makes it tricky for the custom component to manage data in the slotted children and often results in compromised component design just to enable such data sharing to take place.

The other big problem area with conventional slotting and the scoping rules surrounding it, is the creation of custom components that use stamping user interfaces to output sets of data. An example of such a component might be a customized list-view  where the custom component is managing the communications with a REST endpoint that is providing the data, however, you, as the component author, want the consumer to have the ability to shape how each row should be rendered as it's stamped out.  With conventional slotting, this type of use case is really not possible without cheating1 and this is really why templated slots have been introduced.

Introducing Templated Slots

Templated Slots introduce the idea of a data context to a slot, so rather than the components in the slot only seeing the same data from the parent viewModel as the component, they actually see (some selected) data from within the Custom Component as well.  This is a subtle but fundamental difference. Here's a simple example:

<demo-list-o-data data="{{someData}}">
  <template slot="employeeRowTemplate" data-oj-as="employeeRow">
      <span>
        <oj-bind-text value="[[employeeRow.employeeName]]"></oj-bind-text>
      </span>  
  </template>
</demo-list-o-data>

So in the above example the value for employeeName is actually being provided by the demo-list-o-data component itself, not the surrounding page model. 

Breaking Down the Use of Templated Slots

Looking at the above example there are some key things to note:

  1. The root child of a templated slot must be a <template> element as shown here.
  2. The name of the template-slot that this template relates to is indicated by the slot attribute on the template tag. So in this case the template will be used for the employeeRowTemplate slot. Just like normal slots, a templated-slot can be defined as the default slot for the component, in which case, the consumer would not have to define a slot attribute.
  3. The data-oj-as attribute on the template tag defines how the template-slot specific data can be accessed.  In this case, the data in the context of the template will be accessed through a variable called employeeRow.  Functionally this is the same as the as attribute you find on components such as oj-bind-for-each and oj-list-view.  I'll explain how you can actually define that set of data as a component author later. If data-oj-as is not supplied on the template then the data is exposed through a standard variable called $current.
  4. Within the template we can refer to the data provided by the custom component using that oj-data-as alias, hence the reference to employeeRow.employeeName in the <oj-bind-text>. Again, if data-oj-as had not been  supplied then the binding expression would use $current.employeeName instead.

Apart from this, from the perspective of the consumer, using a templated-slot is really just like using any other slot.

Templated-Slots for Component Authors

Now let's look at what it takes to define a templated slot within your components

Metadata

First of all, of course, we want to make it clear to consumers (and tooling such as VBCS) that they are dealing with a templated-slot, not a "normal" slot. This is done by defining a data attribute in the slot metadata that describes the shape of the specific data that the component will be exposing through to the slot contents via $current or the assigned data-oj-as name. For our example here, this is what the component.json slot metadata might look like:

  "slots": {
    "employeeRowTemplate":{
      "description":"Allows the consumer to control how each row in the list is laid out",
      "data": {
        "employeeId": {
          "description":"Unique identifier for the employee",
          "type":"number"
        },
        "employeeName": {
          "description":"First and last name of employee",
          "type":"String"
        }
      }
    }
  }

Note how I also give the user a clue here by appending the word "Template" to the slot name, that's probably a good practice to follow.

In the Component View

Once you have a templated-slot defined you assign it into your component view in much the same way as a conventional slot, except:

  1. You use the <oj-bind-template-slot> tag to act as the marker rather than just <oj-bind-slot>
  2. You set the data attribute on the oj-bind-template-slot to map the data that you want to expose through the data-oj-as / $current variable.  The shape of the data that you expose in this way should, of course, match the shape of the data that you defined in the metadata definition for the templated-slot.

So the a fragment from a view implementation might look a little like this:

<ol>
  <oj-bind-for-each data="[[employees]]">
    <template>
      <li>
        <oj-bind-template-slot name="eachRow" data="{{$current.data}}">
        </oj-bind-template-slot>
      </li>
    </template>
  </oj-bind-for-each>
</ol>

In this case, the templated-slot is being used to provide the row template for a looping/stamping construct (for-each), however, note that that does not have to be the case, a templated slot can be used anywhere in your component view.

Note how the data attribute of the <oj-bind-template-slot> is passed the $current.data of the forEach loop (i.e. the row contents) and that data is then made available through the data-oj-as / $current variable for the template to use. 

As a best practice, the component author should also provide a default implementation of the slot, just in case the user does not supply an implementation.  So expanding the version above we might have:

<ol>
  <oj-bind-for-each data="[[employees]]">
    <template>
      <li>
        <oj-bind-template-slot name="eachRow" data="{{$current.data}}">
          <template data-oj-as="defaultRowImpl">
            <span>
              <oj-bind-text 
                value="[[defaultRowImpl.employeeId + ' ' + defaultRowImpl.employeeName]]">
              </oj-bind-text>
            </span>
          </template>
        </oj-bind-template-slot>
      </li>
    </template>
  </oj-bind-for-each>
</ol>

So that's about it for templated-slots, a small enhancement to the Oracle JET Composite Component Architecture which adds a huge amount of flexibility and power to your custom component toolbox!  You can read more about templated-slots in the JS doc for JET and play with the demo here


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


1No I won't explain how you might try and do this, you shouldn't.

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
Oracle

Integrated Cloud Applications & Platform Services